DecompressionFailureConsumed
warning native on by default PHP ≥ 5.3Group: Probable bugs
Check decompression before consuming data.
Example
<?php
$s=gzdecode($bytes); strlen($s);Reported:
- line 2: Check decompression before consuming data.
Detection
Follow each producer reaching a local read, including results written on an earlier loop turn or before a backward jump. A check of an earlier result does not check a later replacement; a check repeated before every consumption does. Ignore producers belonging to other result contracts when selecting an unchecked result. When several assignments reach a use, each producer is checked on its own: an unguarded decompression result is reported even when the other paths use another API or are guarded. When the producers cannot be tracked within the analysis budget, nothing is reported.
- Report results of gzdecode, gzuncompress, gzinflate or inflate_add reaching strlen/strtoupper without a dominating strict false guard. Follow direct expressions and unchanged locals. Exclude matching encoder/decoder roundtrips with default encoding and omitted or valid literal compression level.
- Error suppression does not change the result: a decompression call wrapped in
@(directly or through the assigned local) is followed like the bare call. - D1b. A strict false guard is a
=== false/!== falsecomparison, with the constant on either side, whose other operand is the tracked local or the parenthesised plain assignment that stored the reaching result. It dominates the consumer when the consumer sits in: theif/elseifbranch,whilebody orforbody (last condition expression) selected by a passing test; a laterelseif/elsearm after a failure test; the matching ternary arm (the else arm of a short ternary after a failure test); the right operand of&&/andafter a passing test or of||/orafter a failure test; or the statements following anifwithout alternatives whose failure branch always terminates. Inside a condition,!inverts the test, a true conjunction establishes each of its operands and a false disjunction refutes each of its operands. The tested value must be the one that reaches the consumer: reassignment between test and use, or a repeated call, is a different value. - Resolve builtin functions, extension classes, method receivers, constants and named arguments. Follow unchanged locals and aliases only within the same bounded lexical scope. Require a reachable path; merge branch facts only when all incoming paths establish them.
- Emit one finding at the violating operation, not one per evidence statement. Rebinding, writes, by-reference escape and unresolved calls that can mutate relevant state remove proof. When the proof is inconclusive, nothing is reported.
Exceptions (no report)
A nested encoder proves matching bytes only when its encoding argument is omitted; argument count does not establish that omission.
- Exclude shadowed builtins, unresolved receivers, incomplete syntax and unknown prerequisite facts. An extension function name alone does not prove an application function is that builtin.
- A dominating valid guard or repair described in Detection prevents the finding. Both legacy noinspection and custos-ignore forms suppress this ID.
assert,match (true)arms and a statement-level$s === false and exit;are not guards under D1b; such code is still reported unless flow analysis independently excludes false.- A value the shared flow analysis proves is not false at the use is never reported, whatever form the proof takes: a truthiness or
empty()test, a loose comparison withfalseornull, a type predicate, or an early exit whoseifalso has anelsebranch that continues. So$s ? strlen($s) : 0and$s != false && strlen($s) > 3are not reported, while the wrong arm or operand of such a test still is. The same holds for a statement that leaves on the failing outcome ($v !== false or exit;,$v or throw …;,$v ?: throw …;), for a failing branch that ends in a call that never returns (a callee declarednever, or a function or a final or private method every path of which throws or exits), for a test stored in a boolean and tested later while neither the boolean nor the tested variable has been written, and, on PHP 8.0 and later outside anytrywith acatch, for a builtin call that returned after receiving the value for a parameter that only accepts arrays, objects or callables: such a call throws aTypeErrorforfalse. Aswitch (true)case is read like the condition of anif/elseifchain; in aswitchon the variable itself with constant labels, the cases after afalseornulllabel and thedefaulthold a value that is neither, as does atruecase. Positions PHP may skip prove nothing: the right side of??=, the arguments of a null-safe call and anything chained after?->. A null-safe call to a never-returning method does not end the path, andf(...)creates a closure without callingf. An arm ofmatch (true)is read like aswitch (true)case; an arm that throws, exits or calls a function that never returns takes no part in what holds after thematch.|and&between two tests are read like||and&&for the outcome that fixes both operands (a false|, a true&), and a strictin_array($v, [...], true)over literals that include neitherfalsenornullexcludes both where it holds.gotojumps are followed, including a jump out of atryorcatchblock, which runs thefinallyblocks it leaves on its way. Where agotolabel is written before the use, and after the place where its scope first names the checked variable, a variable that can hold the same value, or code that may write any variable (extract,compact,get_defined_vars,parse_strgiven one argument, a variable variable,$GLOBALS,eval, an included file), a jump can land between a guard and the use: only what the flow analysis establishes counts there, and a guard recognized by where it is written proves nothing. Two variables can hold the same value when one is written from the other by a plain assignment, an assignment by reference or??=, when aforeachtakes one out of the other, or through a chain of those; in the value assigned, the receiver and the arguments of a call, the operands of an operator other than??, the variables a string embeds and an element key do not count, and a statement that only reads (echo $prefix . $parts['host'];) ties nothing. A parameter is named from the start of the function, and a checked expression that reads$thisor no variable counts every label before it. In a scope the flow analysis does not follow (one that evaluates or includes code, which may write any variable) a guarded use after such a label is therefore reported. A generator is followed: while it is suspended at ayield, the code that consumes it may write only what an unknown call may write, and, when the generator returns by reference, the variable it yields. A label written before all of that, such as aretry:label ahead of thetrythat produces and checks the value, changes nothing, whatever else the function reads beside the checked value; at file level every label written before the use counts. What was established of astaticlocal holds until the next call, or code that runs without a call being written (an object converted to text, a magic property or offset access, the destructor of an object a variable held when it is given another value), that may enter the function again and assign the variable. A call of PHP's own counts as such when it may run code of the project: it is given a callback other than the name of a PHP function that only computes on text and numbers and raises no warning whatever text it is given (a closed list:strlen,trim,intvaland the like, nothexdec,absorchr, which warn about text that is no number), an object it may convert, a class name it loads (method_exists($name, 'm'),is_callable('A::b'),defined('A::B'),class_alias($name, 'B'),new ReflectionClass($name),$arrayObject->setIteratorClass($name)), a class to construct ($statement->fetchObject('Row')) or a filter that one of PHP's ownFILTER_…constants does not name, which may be the one that calls back, or it runs what was registered earlier (an error, session or output-buffer handler, the callbacks of a cURL transfer or an XML parser, an iterator wrapped in another, a filter added to a stream). A warning that a PHP function raises by itself when called directly (hex2bingiven an odd number of digits, a file that cannot be read) is not counted, although the error handler of the project receives it. This limits every guard form of this specification, not only the ones the flow analysis reads.
Fix
No automatic fix. The required repair depends on error handling, data selection, lifecycle ordering or application policy.
PHP versions
Requires PHP 5.3 or later and the referenced extension API. Respect method-specific introduction versions within PHP 5.3–8.5; unsupported optional APIs cannot establish facts. Named arguments require PHP 8.0 or later.
Configure
In custos.json:
{
"rules": {
"DecompressionFailureConsumed": {
"enabled": false
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
// @custos-ignore DecompressionFailureConsumed