diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index a4b1a35bba0..2adeeff3eb6 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1781,6 +1781,120 @@ UPDATE 1
fixed.
+
+ In the examples above, the policy expressions consider only the current
+ values in the row to be accessed or updated. This is the simplest and
+ best-performing case; when possible, it's best to design row security
+ applications to work this way. If it is necessary to consult other rows
+ or other tables to make a policy decision, that can be accomplished using
+ sub-SELECT>s, or functions that contain SELECT>s,
+ in the policy expressions. Be aware however that such accesses can
+ create race conditions that could allow information leakage if care is
+ not taken. As an example, consider the following table design:
+
+
+
+-- definition of privilege groups
+CREATE TABLE groups (group_id int PRIMARY KEY,
+ group_name text NOT NULL);
+
+INSERT INTO groups VALUES
+ (1, 'low'),
+ (2, 'medium'),
+ (5, 'high');
+
+GRANT ALL ON groups TO alice; -- alice is the administrator
+GRANT SELECT ON groups TO public;
+
+-- definition of users' privilege levels
+CREATE TABLE users (user_name text PRIMARY KEY,
+ group_id int NOT NULL REFERENCES groups);
+
+INSERT INTO users VALUES
+ ('alice', 5),
+ ('bob', 2),
+ ('mallory', 2);
+
+GRANT ALL ON users TO alice;
+GRANT SELECT ON users TO public;
+
+-- table holding the information to be protected
+CREATE TABLE information (info text,
+ group_id int NOT NULL REFERENCES groups);
+
+INSERT INTO information VALUES
+ ('barely secret', 1),
+ ('slightly secret', 2),
+ ('very secret', 5);
+
+ALTER TABLE information ENABLE ROW LEVEL SECURITY;
+
+-- a row should be visible to/updatable by users whose security group_id is
+-- greater than or equal to the row's group_id
+CREATE POLICY fp_s ON information FOR SELECT
+ USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user));
+CREATE POLICY fp_u ON information FOR UPDATE
+ USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user));
+
+-- we rely only on RLS to protect the information table
+GRANT ALL ON information TO public;
+
+
+
+ Now suppose that alice> wishes to change the slightly
+ secret> information, but decides that mallory> should not
+ be trusted with the new content of that row, so she does:
+
+
+
+BEGIN;
+UPDATE users SET group_id = 1 WHERE user_name = 'mallory';
+UPDATE information SET info = 'secret from mallory' WHERE group_id = 2;
+COMMIT;
+
+
+
+ That looks safe; there is no window wherein mallory> should be
+ able to see the secret from mallory> string. However, there is
+ a race condition here. If mallory> is concurrently doing,
+ say,
+
+SELECT * FROM information WHERE group_id = 2 FOR UPDATE;
+
+ and her transaction is in READ COMMITTED> mode, it is possible
+ for her to see secret from mallory>. That happens if her
+ transaction reaches the information> row just
+ after alice>'s does. It blocks waiting
+ for alice>'s transaction to commit, then fetches the updated
+ row contents thanks to the FOR UPDATE> clause. However, it
+ does not> fetch an updated row for the
+ implicit SELECT> from users>, because that
+ sub-SELECT> did not have FOR UPDATE>; instead
+ the users> row is read with the snapshot taken at the start
+ of the query. Therefore, the policy expression tests the old value
+ of mallory>'s privilege level and allows her to see the
+ updated row.
+
+
+
+ There are several ways around this problem. One simple answer is to use
+ SELECT ... FOR SHARE> in sub-SELECT>s in row
+ security policies. However, that requires granting UPDATE>
+ privilege on the referenced table (here users>) to the
+ affected users, which might be undesirable. (But another row security
+ policy could be applied to prevent them from actually exercising that
+ privilege; or the sub-SELECT> could be embedded into a security
+ definer function.) Also, heavy concurrent use of row share locks on the
+ referenced table could pose a performance problem, especially if updates
+ of it are frequent. Another solution, practical if updates of the
+ referenced table are infrequent, is to take an exclusive lock on the
+ referenced table when updating it, so that no concurrent transactions
+ could be examining old row values. Or one could just wait for all
+ concurrent transactions to end after committing an update of the
+ referenced table and before making changes that rely on the new security
+ situation.
+
+
For additional details see
and .
diff --git a/doc/src/sgml/ref/create_policy.sgml b/doc/src/sgml/ref/create_policy.sgml
index 4aaeb121028..89d27879b1e 100644
--- a/doc/src/sgml/ref/create_policy.sgml
+++ b/doc/src/sgml/ref/create_policy.sgml
@@ -414,16 +414,8 @@ CREATE POLICY name ON
- When reducing the set of rows which users have access to, through
- modifications to row-level security policies or security-barrier views,
- be aware that users with a currently open transaction may be able to see
- updates to rows that they are theoretically no longer allowed access to,
- as the new policies may not be absorbed into existing query plans
- immediately. Therefore, the best practice to avoid any possible leak of
- information when altering conditions that determine the visibility of
- specific rows is to ensure that affected users do not have any open
- transactions, perhaps by ensuring they have no concurrent sessions
- running.
+ Additional discussion and practical examples can be found
+ in .