Skip to content

VNX-367: Time-of-check Time-of-use (TOCTOU) Race Condition

A file is checked by name and then opened or changed by the same name within ten lines, in C or C++, so another process can swap the file (typically for a symlink) between the check and the use

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-367
Severity Medium
SARIF level warning
Kind sast
Languages C, C++
CWE CWE-367
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C
Tags toctou race-condition cwe-367
Source internal/sast/rules/vnx-367.rego

Overview

VNX-367 maps to CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition. A file is checked by name and then opened or changed by the same name within ten lines, in C or C++, so another process can swap the file (typically for a symlink) between the check and the use. Reported: access(), eaccess() or euidaccess() on a path followed by any open, open64, fopen, freopen, creat, chmod, chown, lchown, truncate or exec* of that path (the access-then-open setuid race); and stat(), lstat(), stat64() or lstat64() on a path followed by a write or permission change on it: open with O_WRONLY/O_RDWR/O_CREAT/O_TRUNC/O_APPEND, fopen with a w/a/+ mode, creat, chmod, chown, lchown or truncate. The same check and use on one line count. The path may be a variable, a member, a std::string’s c_str() or a literal. Not reported: open with O_EXCL or O_NOFOLLOW, a read after a stat, fstat/fchmod on a descriptor (the safe form), a check and a use on different paths, comments and string literals.

Why This Matters

access() and stat() look a path up; the open() that follows looks it up again. Between the two another process can replace the file with a symbolic link, so a setuid program that checked the real user’s permission writes to a file the user could not.

What Gets Flagged

/* FLAGGED: access() check, then open of the same path */
if (access(path, W_OK) != 0)
	return -1;
int fd = open(path, O_WRONLY | O_APPEND);
/* FLAGGED: stat(), then a write-mode fopen */
if (stat(cfg, &st) == 0 && st.st_uid == getuid())
	fp = fopen(cfg, "w");

What Is Not Flagged

int fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
fstat(fd, &st);   /* check the descriptor you opened */

A read after a stat() (the write flags are read from that call’s own text, so a later open(dst, O_CREAT ...) does not count), fstat/fchmod on a descriptor, a check and a use on different paths or in different functions, comments and string literals are not reported.

Remediation

  1. Open first, then check the descriptor with fstat, and change it with fchmod/fchown.
  2. Use O_EXCL and O_NOFOLLOW; in a setuid program drop to the real user’s IDs before opening instead of calling access().

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-367
// nosec vnx-367

Or record an ignore rule: vulnetix ignore add --rule vnx-367 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-367.rego, cli/internal/sast/docs/vnx-367.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.