May 11, 2023·5 min read·engineeringpostgresqlsecuritypermissionsplanner

PostgreSQL 15.3 repairs security context across planning and schema creation

Two May security fixes concern restrictions that failed to follow execution context. Cached row-security plans and CREATE SCHEMA each crossed a privilege boundary.

A cached query plan can outlive the role that caused PostgreSQL to build it. That reuse becomes a security problem when the plan contains a role-specific row-security decision and later execution uses another role. PostgreSQL 15.3 fixes such a failure after inlining a set-returning SQL function, alongside a separate privilege-escalation defect in schema creation.

The May 11 announcement identifies two security vulnerabilities, CVE-2023-2454 and CVE-2023-2455. Releases 15.3, 14.8, 13.11, 12.15 and 11.20 contain the maintenance update. The announcement also cites CVE-2016-2193 as an earlier correction whose coverage missed the inlining scenario. It is background to the new row-security fix, not a third newly disclosed vulnerability in this release.

I prefer PostgreSQL because its relational behavior and operational rules are usually explicit. These fixes concern an important limit of that confidence. A declared restriction only provides protection if the server preserves its meaning through internal query transformations and changes in execution identity.

Inlining must preserve the role-dependent policy

The 15.3 release notes describe the affected function as a set-returning SQL-language function that refers to a table with row-level security policies. PostgreSQL can inline that function into the calling query. Some cached plans produced through that transformation failed to enforce the appropriate policies when another role reused the plan.

The announcement gives two ways this identity change can arise. A security-definer function can cause planning and execution under different authority. A shared initial user can also plan a query that later runs across multiple SET ROLE operations. The issue depends on role-specific policies and reuse under a different role, which is more precise than saying that every query against a protected table bypasses security.

The resulting error can affect reads and modifications. Applying the wrong policy can permit access to rows that should have remained invisible or allow an otherwise forbidden change. The project says only databases that have used CREATE POLICY to define row security are affected by this vulnerability. That condition belongs in the scope because ordinary grants alone do not create the described policy interaction.

Inlining changes how PostgreSQL represents and executes a query without changing its intended meaning. Security conditions are part of that meaning. If a transformation retains a policy decision that depends on an earlier user, the cache has preserved more context than it can safely reuse. The repair restores enforcement across the role changes identified by the project.

The source attributes the report to Wolfgang Walther and the enforcement correction to Stephen Frost and Tom Lane. Its reference to the older CVE is also useful history. The earlier work repaired most interactions between row security and user-ID changes, while this new report identified an uncovered path involving function inlining.

Schema creation could defeat a protected search path

CVE-2023-2454 concerns a different kind of execution context. Within CREATE SCHEMA, objects in the prevailing search path and in the newly created schema could remain visible even inside a called function or script that tried to establish a secure search path. That defeated a protection the privileged code had explicitly requested.

The release notes describe the consequence as hijacking the privileges of a security-definer function or extension script. The announcement states that an attacker with database-level CREATE permission could execute arbitrary code as the bootstrap superuser. Database owners possess that permission by default, and explicit grants can make it available to other users.

Permission to create a schema is therefore an exposure condition, rather than permission equivalent to the authority gained. A deployment can intentionally allow a role to organize objects in a database without intending to let that role run code as the bootstrap superuser. The defect breaks the separation between those permissions when schema creation defeats the privileged code's lookup restrictions.

The correction prevents CREATE SCHEMA from overriding those search-path changes. It is insufficient to describe the remedy solely as writing more careful application SQL, because the vulnerable behavior undermined a protective change already made by the called code. The release credits Alexander Lakhin with reporting the defect.

My view is that these two fixes belong together operationally even though their mechanisms differ. Each restores a security condition after the server enters a different internal context. One concerns the identity relevant to a cached policy, while the other concerns which objects privileged code can resolve.

Maintenance also repairs persistent correctness

The same release fixes a potential corruption involving CREATE DATABASE with STRATEGY WAL_LOG. Improper buffer handling could cause a later modification of the template database's pg_class catalog to be lost. The source database matters here, since describing the issue only as failure to create the new database would miss the affected persistent state.

A separate partition-pruning correction concerns boolean expressions such as IS NOT TRUE. The old behavior could prune a partition containing null values and omit rows from the result. This is a query-correctness issue, and the release notes identify both the predicate shape and the missing-null consequence. It supplies a concrete example of why a minor update includes more than crash prevention.

Generated columns receive another concurrency correction. Under READ COMMITTED, PostgreSQL may reapply a row update to a newer row version after an EvalPlanQual check. Generated columns then need recalculation because a concurrent update may have changed their dependencies. Version 15.3 performs that recomputation rather than carrying forward the earlier derived values.

Applying the update preserves earlier requirements

PostgreSQL describes these update releases as cumulative. For existing 15.x installations, the release does not require a dump and restore. The announcement gives the normal minor-update procedure of shutting down PostgreSQL and replacing its binaries, while directing users who skipped earlier updates to the earlier release notes for any additional steps.

It also announces that PostgreSQL 11 will stop receiving fixes on November 9, 2023. That is a stated support deadline as of this release, not an outcome inferred from a later calendar. An installation remaining on 11 can apply 11.20 now while planning a move to a supported major version before that deadline. The security fixes address current defects, while the announced support limit determines how long that branch will continue receiving future corrections.