fmgr_security_definer() setup and teardown asymmetry

From: Alexander Kukushkin <cyberdemn(at)gmail(dot)com>
To: Pg Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: fmgr_security_definer() setup and teardown asymmetry
Date: 2026-11-04 09:50:51
Message-ID: CAFh8B=m4hV1RT4UCdMRXaiRsPhbyvGJ8URouTaNTtcp_us+kqQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi hackers,

While reading at fmgr_security_definer() function implementation I noticed
that setup and teardown are not mirror images of each other:
GetUserIdAndSecContext(&save_userid, &save_sec_context);
save_nestlevel = NewGUCNestLevel(); /* if proconfig */
SetUserIdAndSecContext(fcache->userid, ...); /* if prosecdef */
apply proconfig;
fmgr_hook(FHET_START, ...);
... call the function ...
AtEOXact_GUC(true, save_nestlevel); /* if proconfig */
SetUserIdAndSecContext(save_userid, save_sec_context);
fmgr_hook(FHET_END, ...);

There are two asymmetries:
1. FHET_START is called with the function's user and proconfig in effect,
but FHET_END is called after both have been undone. A hook therefore sees
different state at END than it saw at START. Whatever a hook sets up at
START can't be undone at the same level at END, because the surrounding
state has already been unwound.
2. The GUC nest level is opened before the user switch but closed before
the user is restored. So the two scopes overlap instead of one nesting
inside the other.

The other places that switch the user and isolate GUC changes, such as
vacuum_rel(), do_analyze_rel(), index_build() and cluster_rel(), are
strictly nested:
SetUserIdAndSecContext(owner, ...);
save_nestlevel = NewGUCNestLevel();
...
AtEOXact_GUC(false, save_nestlevel);
SetUserIdAndSecContext(save_userid, save_sec_context);

The attached patch makes fmgr_security_definer() follow the same shape:
save -> switch user -> GUC level -> proconfig -> START
... call ...
END -> close GUC level -> restore user

- The user switch now happens before NewGUCNestLevel(). Proconfig is still
applied after the switch, so its behaviour doesn't change.
- FHET_END is now called before anything is undone, so it runs in the same
state as FHET_START.
- The error path is unchanged: FHET_ABORT is still called from PG_CATCH,
and (sub)transaction abort still cleans up.

Most commonly used fmgr_hook implementations (sepgsql, pg_hint_plan) only
maintain their own state and don't look at the user or GUCs at END, so
they're unaffected.

Thoughts?
--
Alexander Kukushkin

Attachment Content-Type Size
0001-Make-fmgr_security_definer-setup-and-teardown-symmet.patch text/x-patch 2.0 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Amit Kapila 2026-11-04 10:01:19 Re: Avoid resetting subscription two_phase state to pending when already enabled
Previous Message Jakub Wartak 2026-11-04 09:47:03 Re: [Bug Report + Patch] File descriptor leak when io_method=io_uring