diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out index 2acbc98a70b..3ad5ed64c69 100644 --- a/contrib/amcheck/expected/check_btree.out +++ b/contrib/amcheck/expected/check_btree.out @@ -178,7 +178,8 @@ SELECT bt_index_check('toasty', true); (1 row) -- --- Check that index expressions and predicates are run as the table's owner +-- Check that index expressions and predicates are run as the table's owner, +-- with empty search_path -- TRUNCATE bttest_a; INSERT INTO bttest_a SELECT * FROM generate_series(1, 1000); @@ -186,19 +187,31 @@ ALTER TABLE bttest_a OWNER TO regress_bttest_role; -- A dummy index function checking current_user CREATE FUNCTION ifun(int8) RETURNS int8 AS $$ BEGIN - ASSERT current_user = 'regress_bttest_role', + ASSERT current_setting('search_path') NOT LIKE '%preempt%', + format('ifun(%s) called with current_schemas %s, search_path %s', + $1, current_schemas(true), current_setting('search_path')); + ASSERT "current_user"() = 'regress_bttest_role', format('ifun(%s) called by %s', $1, current_user); RETURN $1; END; $$ LANGUAGE plpgsql IMMUTABLE; CREATE INDEX bttest_a_expr_idx ON bttest_a ((ifun(id) + ifun(0))) WHERE ifun(id + 10) > ifun(10); +BEGIN; +SET LOCAL check_function_bodies = off; +CREATE SCHEMA preempt; +GRANT USAGE ON SCHEMA preempt TO regress_bttest_role; +SET LOCAL search_path = preempt, pg_catalog, public; +CREATE FUNCTION "current_user"() RETURNS name AS $$ + broken +$$ LANGUAGE sql STABLE PARALLEL SAFE STRICT; SELECT bt_index_check('bttest_a_expr_idx', true); bt_index_check ---------------- (1 row) +ROLLBACK; -- Check support of both 1B and 4B header sizes of short varlena datum CREATE TABLE varlena_bug (v text); ALTER TABLE varlena_bug ALTER column v SET storage plain; diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql index e2f47fcfbe1..626bfe9df5c 100644 --- a/contrib/amcheck/sql/check_btree.sql +++ b/contrib/amcheck/sql/check_btree.sql @@ -116,7 +116,8 @@ INSERT INTO toast_bug SELECT repeat('a', 2200); SELECT bt_index_check('toasty', true); -- --- Check that index expressions and predicates are run as the table's owner +-- Check that index expressions and predicates are run as the table's owner, +-- with empty search_path -- TRUNCATE bttest_a; INSERT INTO bttest_a SELECT * FROM generate_series(1, 1000); @@ -124,7 +125,10 @@ ALTER TABLE bttest_a OWNER TO regress_bttest_role; -- A dummy index function checking current_user CREATE FUNCTION ifun(int8) RETURNS int8 AS $$ BEGIN - ASSERT current_user = 'regress_bttest_role', + ASSERT current_setting('search_path') NOT LIKE '%preempt%', + format('ifun(%s) called with current_schemas %s, search_path %s', + $1, current_schemas(true), current_setting('search_path')); + ASSERT "current_user"() = 'regress_bttest_role', format('ifun(%s) called by %s', $1, current_user); RETURN $1; END; @@ -132,8 +136,16 @@ $$ LANGUAGE plpgsql IMMUTABLE; CREATE INDEX bttest_a_expr_idx ON bttest_a ((ifun(id) + ifun(0))) WHERE ifun(id + 10) > ifun(10); - +BEGIN; +SET LOCAL check_function_bodies = off; +CREATE SCHEMA preempt; +GRANT USAGE ON SCHEMA preempt TO regress_bttest_role; +SET LOCAL search_path = preempt, pg_catalog, public; +CREATE FUNCTION "current_user"() RETURNS name AS $$ + broken +$$ LANGUAGE sql STABLE PARALLEL SAFE STRICT; SELECT bt_index_check('bttest_a_expr_idx', true); +ROLLBACK; -- Check support of both 1B and 4B header sizes of short varlena datum CREATE TABLE varlena_bug (v text); diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index ea3eda71586..0f722ec20f6 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -286,6 +286,8 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed, SetUserIdAndSecContext(heaprel->rd_rel->relowner, save_sec_context | SECURITY_RESTRICTED_OPERATION); save_nestlevel = NewGUCNestLevel(); + set_config_option("search_path", "pg_catalog, pg_temp", PGC_USERSET, + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); } else { diff --git a/src/backend/utils/init/usercontext.c b/src/backend/utils/init/usercontext.c index dd9a0dd6a83..1a3abc62ae8 100644 --- a/src/backend/utils/init/usercontext.c +++ b/src/backend/utils/init/usercontext.c @@ -70,6 +70,12 @@ SwitchToUntrustedUser(Oid userid, UserContext *context) * session state. Also set up a new GUC nest level, so that we can * roll back any GUC changes that may be made by code running as the * target user, inasmuch as they could be malicious. + * + * Unlike most use of SECURITY_RESTRICTED_OPERATION, this opts not to + * use RestrictSearchPath(). Calling it would just stop the current + * user from attacking "userid", but we've already established that + * the current user could just SET ROLE to "userid". Calling it would + * be a compatibility break. */ sec_context |= SECURITY_RESTRICTED_OPERATION; SetUserIdAndSecContext(userid, sec_context);