Skip to content

pqc: initialize the PQC subsystem eagerly in the postmaster - #1

Merged
dyber-pqc merged 1 commit into
mainfrom
fix/eager-pqc-init
Jul 20, 2026
Merged

dyber-pqc merged 1 commit into
mainfrom
fix/eager-pqc-init

Conversation

@dyber-pqc

Copy link
Copy Markdown
Owner

Problem

On a build with liboqs linked and fully functional, the postmaster still logs at startup:

FortressQL: PQC subsystem initialized (liboqs available: no)

This is a false negative. liboqs is present and works — for example SELECT pqc_sig_keygen('ML-DSA-65') returns a valid keypair, and ldd .../bin/postgres shows liboqs.so.

Root cause

pqc_preflight_check() (called from the postmaster) logs the status via pqc_is_available(), which returns the static pqc_initialized flag. That flag is set only by pqc_init() (which calls OQS_init()), and pqc_init() was invoked lazily — only from the KEM/SIG paths in pqc_kem.c / pqc_sig.c on first use.

So at startup the preflight check reads the flag before anything triggers the lazy init, and logs no. Because the flag is process-local and the postmaster never initializes it, each backend also re-initializes liboqs independently on first PQC use, rather than inheriting an initialized state across fork().

Fix

Initialize the subsystem once, eagerly, in pqc_preflight_check() before logging the status. pqc_init() is idempotent, so this is safe, and forked backends inherit the initialized state.

One line of behavior change:

+	(void) pqc_init();
 	/* Log PQC subsystem status */

Verification

Built FortressQL (PostgreSQL 17, -Dpqc=enabled -Dssl=openssl) against liboqs 0.16.0 on OpenSSL 3.5. Before the fix the postmaster logged liboqs available: no; after it logs:

FortressQL: Post-Quantum Cryptography initialized (liboqs)
FortressQL: PQC subsystem initialized (liboqs available: yes)

Note: this relies on the standard fork-based backend model (children inherit the initialized state). An EXEC_BACKEND build would still initialize per backend on first use via the existing lazy path, which is unchanged.

pqc_preflight_check(), called from the postmaster at startup, logs
"PQC subsystem initialized (liboqs available: %s)" using pqc_is_available(),
which reflects the static pqc_initialized flag. That flag is only set by
pqc_init() (which calls OQS_init()), and pqc_init() was invoked lazily —
only from the KEM/SIG paths in pqc_kem.c / pqc_sig.c on first use.

As a result the preflight check ran before any lazy init and logged
"liboqs available: no" on a build where liboqs was linked and fully
functional (e.g. pqc_sig_keygen('ML-DSA-65') returns a valid keypair).
The flag is also process-local, so with no init in the postmaster every
backend re-initialized liboqs independently on first PQC use rather than
inheriting an initialized state across fork().

Initialize the subsystem once, eagerly, in the preflight check so the
logged status is accurate and forked backends inherit the initialized
state. pqc_init() is idempotent, so this is safe.
@dyber-pqc
dyber-pqc merged commit 4746096 into main Jul 20, 2026
1 of 5 checks passed
@dyber-pqc
dyber-pqc deleted the fix/eager-pqc-init branch July 20, 2026 11:29
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.

1 participant