FileLockFailureUnchecked
warning native on by defaultGroup: Probable bugs
Detect a write following flock requiring an exclusive lock where acquisition success is not established.
Example
<?php
function bad($path) { $h = fopen($path, 'cb'); if ($h === false) { return; } flock($h, LOCK_EX); fwrite($h, 'next'); fclose($h); }Reported:
- line 2: Acquire the lock successfully before writing.
Detection
Report a write following flock requiring an exclusive lock where acquisition success is not established.
Resolve builtin calls and named arguments before checking the contract. Project-local callees are considered only when their bodies or immutable summaries establish the required facts. The default is enabled, with positive proof required.
For flow-dependent conditions, require a reachable source-to-use path with no intervening invalidation. Unknown calls, ambiguous aliases, recursion or analysis-budget exhaustion cannot establish definite facts.
D4. Acquisition is established when the write can only run after the last
flockcall on the handle returned true. The result may be tested directly or through a plain local that still holds it unchanged, and the test may be negated, error-suppressed (@), or compared strictly or loosely withtrueorfalse. Accepted tests are:- an
if,elseiforwhilecondition whose guarded body contains the write, or the matching side of a conditional expression; - the right operand of
&&/and(result true) or||/or(result false) containing the write; - an
ifwhose failure branch always leaves (return, throw, exit, break, continue), with the write later in the statement list that owns theif; - a statement of the form
flock(...) or exit/|| throw, with the write later in the same statement list; - a
whileordo-whileretry loop repeating until the result is true and containing nobreak, with the write after the loop.
- an
A test proves nothing, and the write is still reported, when the local was reassigned or merged from another value, when the test sits on only one path to the write (a nested conditional, another closure or function, an earlier
caselabel), when the failure branch can continue, or when the result is combined with an unrelated condition that can let the failing side through.assert(flock(...))is not a test by default: assertions may be compiled out. Withflow.assertionsenabled, a direct native flock result with effect-free arguments proves acquisition on the assumed successful path.An exclusive-lock attempt inside a direct native assertion remains a possible unchecked attempt when assertion arguments may be skipped. Report a following write only while the handle has the same complete allocation identity and no intervening call makes that witness ambiguous. This witness proves no successful acquisition and does not change the common-state query used for definite operations.
Exceptions (no report)
- Guarded acquisition and retry until success in any of the D4 forms, explicit unlocked best-effort contracts and unrelated handles are excluded.
- Unresolved targets, incomplete required facts, suppressed findings and PHP versions lacking the referenced API or language feature produce no report. User declarations shadowing builtin names are not builtin contracts.
Fix
No automatic fix: lock failure and retry policies are unknown.
PHP versions
Apply only on PHP versions supporting the referenced APIs and syntax (within PHP 5.3–8.5). Builtin behavior and argument contracts follow the configured PHP version. Extension-specific calls require resolved extension API symbols; do not treat same-named application functions as extension calls.
Options
None. Enable or disable this rule through normal rule configuration.
Configure
In custos.json:
{
"rules": {
"FileLockFailureUnchecked": {
"enabled": false
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
// @custos-ignore FileLockFailureUnchecked