November 9, 2017·4 min read·engineeringpostgresqlsecurityoperationscorrectness

PostgreSQL 10.1 fixes startup log ownership and upsert privileges

PostgreSQL 10.1 closes security gaps in sample startup scripts, upsert permission checks, and JSON record conversion. It also identifies BRIN indexes that may require rebuilding.

Opening a log file before dropping root privileges can give the database operating-system user a way to damage a file it cannot otherwise write. PostgreSQL's November 9 maintenance announcement includes a fix for that ordering in its sample startup scripts. The problem is outside SQL execution, but it crosses a privilege boundary around the database process.

PostgreSQL 10.1 ships alongside updates for supported older branches. The release fixes three security issues and several correctness defects, including a BRIN indexing race that may require existing indexes to be rebuilt. Its operational implications depend on which code and stored state an installation actually uses.

I prefer PostgreSQL's explicit accounting of that scope. A database installation includes startup scripts and client utilities as well as the server executable. Updating a package cannot necessarily replace a script somebody copied and modified, and it cannot automatically reconstruct every index affected by an earlier race.

The log must open under the database user's authority

The 10.1 notes at the release tag explain CVE-2017-12172. The sample server-start scripts opened the postmaster log while still running as root. A database owner could make the configured log path a symbolic link to another user's file. Appending startup messages would then corrupt that file using authority the database owner did not possess.

The corrected scripts become the configured database operating-system user before opening the log. File access therefore occurs under the account intended to run PostgreSQL. Changing the order removes the unnecessary root privilege from the operation that follows a path controlled by the less privileged user.

This is a distinction between authority over PostgreSQL's operating environment and authority over the host. Being able to configure the database log should not grant the ability to append arbitrary content to root-protected files. The script had unintentionally combined those capabilities by performing the open too early.

These are sample scripts, and the notes say they are not installed anywhere by default. Users who adopted them need to recopy the corrected scripts or apply equivalent changes to their modified versions. That limits the affected population without making the consequences smaller for installations that use them.

An existing root-owned log also needs attention before restarting with the corrected script. The tagged notes say to remove it or rename it out of the way. Once the script opens the log as the database user, leaving an inaccessible old file in place can prevent the corrected startup path from working. Ownership and script replacement are related parts of the remediation.

Upsert has to check the columns used to resolve a conflict

CVE-2017-15099 concerns the update path of INSERT ... ON CONFLICT DO UPDATE. Resolving a conflict uses an arbiter index, and that path requires SELECT permission on the arbiter's columns. PostgreSQL failed to check this permission when the arbiter was specified by constraint name.

The syntax used to identify the arbiter should not decide whether the privilege check happens. Naming a constraint and identifying the relevant indexed columns are ways to express conflict resolution. Neither form should bypass the read permission required by the update path. The correction brings the named-constraint case under the intended table permission checks.

The same security issue includes a separate omission for row-level security. Updated rows were not checked against the table's SELECT policies, regardless of how the arbiter was specified. That is broader than the named-constraint case and matters for tables whose visibility rules differ among users.

An upsert contains more authorization questions than whether the caller can insert a new row. Its alternative path can inspect a conflicting row and update it. Permission and policy checks must cover that path as well. Combining insertion and conflict handling in one statement does not remove the underlying actions' access requirements.

I find this especially relevant to application roles with restricted permissions. A convenient write statement still needs a precise account of which existing data it may inspect and change. The release notes describe both the missing column privilege check and the missing row-policy check, so the fix should not be reduced to a generic statement about upsert security.

JSON conversion must agree on the result row type

CVE-2017-15098 affects json_populate_recordset() and jsonb_populate_recordset(). They used the row type specified in a FROM ... AS clause without checking that it matched the actual row type of the supplied tuple value. A mismatch usually caused a crash, with disclosure of server memory also considered possible.

The issue is a disagreement between two descriptions of the same result. Code that interprets a tuple according to an incompatible row layout can read the wrong locations. Checking the relationship between the supplied value and declared result type is therefore necessary before treating the result as a correctly formed recordset.

The tagged wording is careful about consequences. Crashes are the usual result described there, while memory disclosure is a possibility. The security announcement names the disclosure risk, but neither source supports inventing a demonstrated extraction method or claiming every malformed call returns protected data.

BRIN summarization has a separate repair requirement

The release fixes a race between BRIN summarization and concurrent table extension. Some rows could be omitted from the index. The notes say existing BRIN indexes may need reindexing to recover from past occurrences. Installing the fix prevents that faulty execution but does not prove that previously built summaries include every required row.

Another BRIN correction addresses concurrent-update races that could produce errors such as invalid index offnum or inconsistent range map. These entries concern related code with distinct symptoms. Omitted rows and explicit index errors should not be treated as the same observable failure.

PostgreSQL 10.1 needs no dump and restore for an existing 10-series installation. The announcement urges affected users to update promptly, while specifically directing users of BRIN indexes and the sample startup scripts to post-upgrade instructions. Those instructions identify persistent state and copied operational code that remain outside a straightforward replacement of server binaries.