CommitFest 7261 — PQpassfileLookup Review

report · generated from canonical Markdown

Type
report
Source
postgresql-master
Slug
commitfest-7261-review
Tags
commitfest-7261libpqpassfilepg-review-harnessready-for-committerreview-report-v2
Updated
2026-09-14T00:30:43.214006+00:00
Verdict / Summaryready-for-committer
Front matter
schema: review-report-v2
target: CommitFest 7261: PQpassfileLookup libpq passfile exposure
base: 0c5d6269614e107d1d2d669f82f63f7e232b30c9
head: 0c5d6269614e107d1d2d669f82f63f7e232b30c9
verdict: ready-for-committer
blocker_counts: verified=0 unverified=0
scope_identity: 4d4ae07c2c9a409f50caf5a7fa753ed887c47435b14f447dd007e76e29c33fb3
model: glm-5.2:cloud
source: postgresql-master
slug: commitfest-7261-review

CommitFest 7261 — PQpassfileLookup() libpq passfile exposure

Patch summary

Adds a new public libpq API PQpassfileLookup(hostname, port, dbname, username, passfile) exposing the existing static passwordFromFile() pgpass lookup so applications connecting through an intermediary (e.g. local SSH tunnel) can look up the password under the real server's host/port and then feed it to PQconnectdb as the password parameter. NULL/empty hostname→localhost (incl. default socket dir), NULL/empty port→DEF_PGPORT_STR, NULL/empty dbname or username→no password. NULL/empty passfile→PGPASSFILE env or the default .pgpass file location. Returns malloc'd string (free with PQfreemem) or NULL. Adds LIBPQ_HAS_PASSFILE_LOOKUP feature macro, export PQpassfileLookup 212, SGML docs, --passfile mode in libpq_testclient, and TAP test t/007_passfile.pl (13 subtests).

Findings summary

IDSeverityClassVerificationRepair validation
F-001MINORcorrectnessverifiedpassed (compile + test + doc-build)
F-002NITmaintainabilityverifiedpassed (compile + static-check + doc-build)
F-003NITtestsverifiedpassed (targeted-test, 15/15 subtests)
S2MINORsecurityverifiedpassed (doc-build + compile)
S3NITsecurityrefutednot-applicable-refuted (accepted by design)
COV-1MINORtestsverified (verified-by-coverage)passed (compile + targeted-test, 15/15 subtests)

Blockers: 0. Majors: 0. Minors: 3 (F-001, S2, COV-1). Nits: 3 (F-002, F-003, S3-refuted).

Verification Summary

All 6 findings were verified against source by the pg-finding-verifier seat in disposable worktrees (HEAD 0c5d6269614e107d1d2d669f82f63f7e232b30c9, scope identity 4d4ae07c...). Validation methods: apply-check, diff-check, compile (meson+ninja), targeted-test (meson test libpq/007_passfile), doc-build (ninja doc/src/sgml/postgres.html), and coverage-delta (gcov). S3 was refuted (accepted by design, not a defect requiring repair). The combined F-001/F-002/S2 repair patch (SHA256 d0a57deebbe1ed6f7d77977a2a1438b7bf3a3cba3a401c0250e757cd5ecdfacb) compiles clean, passes all 13 existing TAP subtests, and the doc-build contains the new "residual plaintext"/"out-of-memory" text. The F-003 test repair (SHA256 499c841be9ef2699ce1bad206ef0287ca6b7ed66b11c13a9727f0216c218dc2c) grows the suite to 15 subtests, all passing. The COV-1 test repair (SHA256 327daba51caf5c671e9e8c42e5ede6632db6a18ffb00625b974d2dca4f511516) also yields 15/15 subtests and covers the home-directory fallback path.

Repair Validation Summary

PatchSHA256FindingsApplyDiffCompileDoc-buildTestStatus
repair_f001_f002_s2d0a57deeb...F-001, F-002, S2OKOKcleanclean13/13passed
repair_f003499c841be...F-003OKOKn/a (perl)n/a15/15passed
repair_cov1327daba51...COV-1OKOKcleann/a15/15passed
(S3)n/aS3refutedrefutedrefutedrefutedrefutednot-applicable-refuted

All patches apply cleanly on top of the staged diff (HEAD 0c5d6269614e107d1d2d669f82f63f7e232b30c9). All validation performed in disposable worktrees; the reviewed worktree was never modified.

Changed-Code Coverage

Raw metrics

MetricCount
Changed executable lines30
Covered changed lines23
Compiler-uncovered changed lines7
Structural uncovered changed lines0
Changed lines without coverage coordinates0
Changed branches20
Covered changed branches13

Line coverage: 23/30 = 76.7%. Branch coverage: 13/20 = 65.0%.

Touched functions

FunctionFileCalledBlocks executedLine covBranch cov
PQpassfileLookupfe-connect.c1264%8/128/10
test_passfile_lookuplibpq_testclient.c1288%13/167/8
mainlibpq_testclient.c1256%2/31/2

Actionable uncovered regions

UR-1: fe-connect.c:8190,8192-8194 — home-directory fallback (requires-special-harness)

c
8190: 			char		homedir[MAXPGPATH];
8192: 			if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
8193: 				return NULL;
8194: 			snprintf(pgpassfile, sizeof(pgpassfile), "%s/%s",

Why missed: the TAP test always supplies an explicit passfile or sets PGPASSFILE. The home-dir fallback (passfile=NULL/empty + PGPASSFILE unset) is never entered. Requires environment injection (controllable $HOME) and platform guards.

Test repair: platform-guarded TAP block (SKIP on Windows) with local %ENV deleting PGPASSFILE and setting HOME to a tempdir containing .pgpass, plus a no-HOME variant. Validated: 15/15 subtests; lines 8190, 8192, 8194 now covered; line 8193 remains uncovered (pqGetHomeDirectory-fails branch, infeasible without a UID with no passwd entry).

COV-1 finding issued (MINOR tests, verified-by-coverage) — home-directory fallback of PQpassfileLookup is untested. Repair patch SHA256: 327daba51caf5c671e9e8c42e5ede6632db6a18ffb00625b974d2dca4f511516.

UR-2: libpq_testclient.c:38-39 — usage error (coverable-now)

c
38: 		fprintf(stderr, "usage: libpq_testclient --passfile PASSFILE HOSTNAME PORT DBNAME USERNAME\n");
39: 		return 1;

Trivially coverable by calling libpq_testclient --passfile with too few args. Test example provided (TAP). Not issued as a standalone COV finding — test-harness usage guard, not production logic.

UR-3: libpq_testclient.c:69 — fallback message (coverable-now)

c
69: 	printf("currently only --ssl and --passfile are supported\n");

Coverable by calling with an unknown subcommand. Test example provided (TAP). Not a standalone COV finding — test-harness fallback, not production logic.

Uncovered branches

BranchFile:lineDispositionTest example
8182:branch2fe-connect.crequires-special-harnessCOV-1 repair
8186:branch1fe-connect.crequires-special-harnessCOV-1 repair
8186:branch3fe-connect.crequires-special-harnessempty-PGPASSFILE variant
8192:branch1fe-connect.crequires-special-harnessCOV-1 repair
8192:branch2fe-connect.cinfeasible-with-reasonpqGetHomeDirectory failure (waived: not-instrumentable)
36:branch0libpq_testclient.ccoverable-nowtoo-few-args test
66:branch1libpq_testclient.ccoverable-nowunknown-subcommand test

Exclusion totals

Non-instrumented changes

Residual limitations

pgsql-hackers thread ledger

Aggregated Test Plan

TestVehicleFilesStatus
Existing t/007_passfile.pl (13 subtests)TAPsrc/interfaces/libpq/t/007_passfile.plpassing (baseline)
F-003 repair (literal DEF_PGPORT_STR port)TAPsrc/interfaces/libpq/t/007_passfile.pl15/15 (proposal, user-controlled apply)
COV-1 repair (home-dir fallback)TAPsrc/interfaces/libpq/t/007_passfile.pl15/15 (proposal, user-controlled apply)
F-001/S2 repair (OOM stderr + doc zeroing)C+SGMLfe-connect.c, libpq.sgml13/13 + doc-build clean (proposal, user-controlled apply)

Per-finding detail

F-001

— MINOR correctness — swallowed OOM errmsg

PQpassfileLookup passes &errmsg to passwordFromFile but never inspects it. On OOM, passwordFromFile sets *errmsg="out of memory" (lines 8086, 8127) and returns NULL; the public API returns NULL silently. Repair: capture return, fprintf(stderr, "%s\n", errmsg) on non-NULL errmsg; add doc note. Patch SHA256: d0a57deeb....

F-002

— NIT maintainability — blank-line style drift

Two blank lines between passwordFromFile/PQpassfileLookup and PQpassfileLookup/pgpassfileWarning; surrounding code uses one. Repair: collapse to single blank lines (part of combined patch).

F-003

— NIT tests — implicit-only default-port coverage

NULL/empty port→DEF_PGPORT_STR only tested against * wildcard, not a literal default-port entry. Repair: read DEF_PGPORT_STR from pg_config.h, add literal localhost:5432:defportdb:defuser:defportpw entry, add two assertions. Patch SHA256: 499c841be....

S2 — MINOR security — credential residue

Returned password is malloc'd by strdup; PQfreemem is bare free() with no zeroing. Internal path uses explicit_bzero before free. Repair: add SGML note recommending explicit_bzero before PQfreemem. Part of combined patch.

S3 — NIT security — refuted (accepted by design)

Silent credential enumeration without a connection is the intended feature, guarded by the same file-permission checks (0600, plain-file). No fix required.

COV-1

— MINOR tests — home-directory fallback untested

The new public API documents default home-directory password file resolution, but the TAP suite never exercises it. 4 lines (8190, 8192-8194) and 5 branches uncovered. Repair: platform-guarded TAP block with controllable $HOME. Patch SHA256: 327daba51....

Deterministic verdict

Provenance / model routes

SeatModelHandoff slug
pg-patch-reviewerglm-5.2:cloudcommitfest-7261-review-handoff-patch-reviewer-r2
pg-security-reviewerglm-5.2:cloudcommitfest-7261-review-handoff-security-reviewer-r2
pg-test-authorglm-5.2:cloudcommitfest-7261-review-handoff-test-author-r2
thread-reconcilerglm-5.2:cloudcommitfest-7261-review-thread-ledger-r2
pg-finding-verifierglm-5.2:cloudcommitfest-7261-review-handoff-verifier-r2
pg-change-coverage-reviewerglm-5.2:cloudcommitfest-7261-review-handoff-coverage-r2
pg-finding-verifier (COV)glm-5.2:cloudcommitfest-7261-review-handoff-cov-verifier-r2

Verified-clean section

No findings were refuted as false positives except S3, which is accepted by design (not a defect). All other findings were verified as real and carry concrete repair patches validated in disposable worktrees.