Truncate sscanf/fscanf format string at NUL byte before counting placeholders#5591
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Open
Truncate sscanf/fscanf format string at NUL byte before counting placeholders#5591phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
sscanf/fscanf format string at NUL byte before counting placeholders#5591phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
…placeholders
- In PHP's sscanf/fscanf, a NUL byte (\0) in the format string terminates
parsing because the C implementation treats it as end-of-string. PHPStan
was not accounting for this and counted placeholders after the NUL byte.
- Truncate format string at the first NUL byte in PrintfHelper::parsePlaceholders()
when $isScanf is true, fixing both parameter count validation and placeholder
parsing.
- Apply the same NUL truncation in SscanfFunctionDynamicReturnTypeExtension
before matching format specifiers for return type inference.
- Restructure the extension to return array{}|null when no specifiers are found
(e.g. format starts with NUL) instead of falling through to the generic
signature return type.
- Verified that printf/sprintf do NOT truncate at NUL (C's snprintf processes
NUL as data), so no changes needed for printf-family functions.
|
@phpstan-bot: you are overcomplicating it, no truncation is necessary. the number of placeholders that capture a value in the format parameter of the *scanf() function family is the count() of the array cast return value of the sscanf() operation with an empty string (and naturally the same format string without any NUL truncation). NOTE: error handling applies: throwing errors for the function call for PHP < 8 must be provoked to capture the error cases of PHP 8+ ValueError. The error case is the error message. |
|
please use the following five test-cases to verify the count and error values: |
Contributor
|
@phpstan-bot check comments from @hakre |
hakre
added a commit
to hakre/phpstan-src
that referenced
this pull request
May 3, 2026
Make `public function getScanfPlaceholdersCount(string $format): ?int` returning the sscanf() vetted number of placeholders that give/return/assign conversions. refs: - phpstan#5591 - phpstan/phpstan#14567
hakre
added a commit
to hakre/phpstan-src
that referenced
this pull request
May 3, 2026
Make `public function getScanfPlaceholdersCount(string $format): ?int` returning the sscanf() vetted number of placeholders that give/return/assign conversions. refs: - phpstan#5591 - phpstan/phpstan#14567
hakre
added a commit
to hakre/phpstan-src
that referenced
this pull request
May 3, 2026
Make `public function getScanfPlaceholdersCount(string $format): ?int` returning the sscanf() vetted number of placeholders that give/return/assign conversions. refs: - phpstan#5591 - phpstan/phpstan#14567
hakre
added a commit
to hakre/phpstan-src
that referenced
this pull request
May 3, 2026
Make `public function getScanfPlaceholdersCount(string $format): ?int` returning the sscanf() vetted number of placeholders that give/return/assign conversions. refs: - phpstan#5591 - phpstan/phpstan#14567
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In PHP's
sscanf/fscanf, a NUL byte (\0) in the format string terminates parsing because the underlying C implementation treats it as end-of-string. PHPStan was not accounting for this, causing it to count placeholders after the NUL byte, leading to incorrect parameter count validation and wrong return type inference.Changes
src/Rules/Functions/PrintfHelper.php: Truncate the format string at the first NUL byte inparsePlaceholders()when$isScanfis true. This fixes parameter count validation for bothsscanfandfscanf.src/Type/Php/SscanfFunctionDynamicReturnTypeExtension.php: Truncate format string at NUL byte before matching specifiers. Also restructured to returnarray{}|nullwhen no specifiers are found (instead of falling through to the generic return type).Analogous cases probed and found to be already correct
printf/sprintf/fprintf/vprintf/vsprintf: Confirmed PHP does NOT truncate format strings at NUL for printf-family functions (NUL is treated as data). No changes needed.PrintfParameterTypeRule: Only handlesprintf/sprintf/fprintf, not scanf functions. Not affected.PrintfArrayParametersRule: Only handlesvprintf/vsprintf. Not affected.%*) inSscanfFunctionDynamicReturnTypeExtension: Already handled correctly by accident — the regex naturally doesn't match%*d/%*spatterns since*is neither a digit nor a specifier character.Root cause
PHP's
sscanf/fscanfcalls C'ssscanfinternally, which treats\0as end-of-string in the format. PHPStan's format parsing (both the parameter count rule and the return type extension) operated on the full PHP string value without truncating at NUL, so placeholders like%dappearing after a\0were incorrectly counted.Test
tests/PHPStan/Rules/Functions/data/bug-14567.php+PrintfParametersRuleTest::testBug14567()— verifies thatsscanf/fscanfcalls with NUL bytes in the format string pass parameter count validation when the correct number of arguments (based on pre-NUL placeholders) is provided.tests/PHPStan/Analyser/nsrt/bug-14567.php— verifies thatsscanf/fscanfreturn type inference correctly ignores placeholders after NUL bytes, including edge case where NUL is at the start of the format.Fixes phpstan/phpstan#14567