Skip to content

libraries: Avoid check-then-use races on file operations - #7710

Open
cwhite911 wants to merge 2 commits into
OSGeo:mainfrom
cwhite911:toctou-prechecks
Open

cwhite911 wants to merge 2 commits into
OSGeo:mainfrom
cwhite911:toctou-prechecks

Conversation

@cwhite911

Copy link
Copy Markdown
Contributor

Addresses 11 of the 14 CodeQL cpp/toctou-race-condition alerts. These are check-then-use patterns on per-user mapset and config files, so none is a privilege boundary; the changes are correctness/robustness cleanups that also make the code simpler:

  • lib/vector (Vlib/open.c, open_nat.c): access() before unlink() of topo/sidx/cidx/fidx support files. unlink() on a missing file is harmless, so the precheck is gone (a code comment in open_nat.c already asked "remove this check?").
  • lib/gis (get_projinfo.c): access() before fopen() in G_get_projwkt()/G_get_projsrid(). Now fopen() runs first and ENOENT takes the old "not found" path (including the PROJ_EPSG fallback); other errors stay fatal. Verified in both a projected project (WKT returned) and an XY project (returns NULL, no error).
  • lib/gis (mkstemp.c): the access() existence probe is now only used for the name-only G_mktemp() variant (which is inherently just a probe, as its docs note); the creating variants rely solely on the existing O_CREAT|O_EXCL + EEXIST handling.
  • display/d.mon (start.c): delete an existing output file based on unlink() errno instead of access() first; behavior (warning with --overwrite, fatal when undeletable) is unchanged.
  • raster/r.li (daemon.c): open the configuration file first, then size the read buffer with fstat() on the open descriptor instead of stat() by path.
  • lib/db (login.c): the dblogin file is now created with owner-only (0600) permissions atomically via open(O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR) + fdopen() instead of fopen() followed by chmod(), so it never exists with wider permissions. The chmod() remains to tighten a pre-existing file. Verified: db.login produces a -rw------- file with correct content. (MSVC keeps plain fopen(), matching the existing #ifndef _MSC_VER treatment of chmod.)

The remaining 3 alerts (lib/init/clean_temp.c) are left as is: the stat() result there decides file-vs-directory handling for the user's own tmp entries, the return values are already checked, and restructuring would not remove the race meaningfully. I propose dismissing those with justification.

Verification: all five directories compile; runtime checks exercised each path (v.random + v.build topo rebuild, g.proj -w in projected and XY projects, d.mon start over an existing file with --overwrite, r.li.patchdensity with a conf file, db.login).

This is part of a larger effort to work through the open code scanning alerts, grouped into small PRs by issue type.

Written with the assistance of Claude Code.

Remove access() prechecks before unlink() and fopen() and handle the
result of the actual call instead: Vlib deletes support files
unconditionally, G_get_projwkt and G_get_projsrid distinguish ENOENT
from other fopen errors, d.mon deletes an existing output file based
on unlink errno, and G__mkstemp only uses access() for the
name-only (no-create) variant, relying on O_CREAT|O_EXCL otherwise.
r.li now opens the configuration file first and sizes the buffer with
fstat(). The dblogin file is now created with owner-only permissions
atomically via open() instead of fopen() followed by chmod().

Fixes 11 CodeQL cpp/toctou-race-condition alerts.
Comment thread lib/db/dbmi_base/login.c Fixed
@github-actions github-actions Bot added vector Related to vector data processing raster Related to raster data processing C Related code is in C database Related to database management libraries module display labels Jul 14, 2026
CodeQL flagged the chmod(file, ...) as a time-of-check/time-of-use
race: the file was already opened, but the permissions were then set
by path, which could be redirected between the two operations. Set the
permissions on the open descriptor with fchmod() instead. A newly
created file already gets 0600 from open(O_CREAT, S_IRUSR | S_IWUSR)
above; fchmod tightens a pre-existing file that open(O_TRUNC) leaves at
its old mode, same as before but without the race.

Fixes the CodeQL cpp/toctou-race-condition alert this PR reintroduced.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C Related code is in C database Related to database management display libraries module raster Related to raster data processing vector Related to vector data processing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants