Skip to content

docs: document the unlogged-table fork tweak limitation (M5)#20

Merged
jdatcmd merged 1 commit into
mainfrom
docs/unlogged-fork-tweak-limitation
Jul 16, 2026
Merged

docs: document the unlogged-table fork tweak limitation (M5)#20
jdatcmd merged 1 commit into
mainfrom
docs/unlogged-fork-tweak-limitation

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

M5: XTS tweak aliasing for unlogged-table init/main forks

The security review's M5 finding: CalcBlockIv maps INIT_FORKNUM to MAIN_FORKNUM, so an unlogged table's init and main forks reuse the same XTS (key, tweak) at each block position.

Why this is required, not a bug

PostgreSQL resets an unlogged relation after a crash by raw-copying the init fork file over the main fork (copy_file() in src/backend/storage/file/reinit.c), a filesystem-level copy of already-encrypted bytes that never passes through the storage hooks. For that copy to decrypt correctly as the main fork, the init fork must be encrypted with the main fork's tweak. The aliasing is therefore functionally required by PostgreSQL's own reset mechanism.

The tradeoff

The two forks share the XTS tweak per block, so their ciphertext reveals which main-fork blocks are byte-identical to the (small, known) init-fork template. This is:

  • limited to unlogged tables,
  • a confidentiality-only XTS property (block equality, not contents),
  • with no effect on regular logged tables.

Decision

A real fix would require an invasive new core-patch hook in reinit.c to re-encrypt on reset, plus per-relation on-disk format versioning and migration across PG 16/17/18. Given the leak is narrow and the aliasing is mandated by PostgreSQL's reset, this documents M5 as an accepted, confidentiality-only limitation instead, consistent with how the review treats XTS's lack of integrity.

  • documentation/docs/concepts/threat-model.md: new "Unlogged table forks" entry under what open_pg_tde does not protect against, plus a summary-table row.
  • src/encryption/enc_tde.c: expands the CalcBlockIv comment with the security rationale.

🤖 Generated with Claude Code

An unlogged relation's init fork is reset onto the main fork by PostgreSQL
with a raw copy_file() in reinit.c, outside the storage hooks. For that
copy of encrypted bytes to decrypt as the main fork, CalcBlockIv encrypts
the init fork with the main fork's XTS tweak (INIT_FORKNUM aliases
MAIN_FORKNUM). The tradeoff is that the two forks share the tweak at each
block, so an attacker with the data files can tell which main-fork blocks
are byte-identical to the init-fork template.

This is required by PostgreSQL's own unlogged-relation reset and is a
confidentiality-only limitation of XTS, so it is documented in the threat
model rather than changed. Also expands the CalcBlockIv comment with the
security rationale.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jdatcmd jdatcmd merged commit c9f7e1b into main Jul 16, 2026
25 of 37 checks passed
@jdatcmd jdatcmd deleted the docs/unlogged-fork-tweak-limitation branch July 16, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants