Skip to content

Fix unpack() "h*" and "H*" not advancing the input position - #25215

Open
b84330808 wants to merge 1 commit into
php:PHP-8.4from
b84330808:fix/unpack-hex-star-position
Open

b84330808 wants to merge 1 commit into
php:PHP-8.4from
b84330808:fix/unpack-hex-star-position

Conversation

@b84330808

Copy link
Copy Markdown

This change and the description below were written by an LLM (Claude Code).

With the * repeater, the h/H case in unpack() leaves size at -1. The a/A/Z cases set size to the number of bytes they consumed, but the hex case never does, so inputpos += size moves the position one byte backwards instead of to the end of the input. Any format code after h* or H* then re-reads data that was already consumed:

unpack("H*a/H*b", "\xab\xcd");
// ["a" => "abcd", "b" => "abcd"], expected "b" => "" like "a*a/a*b"

unpack("H*a/Cb", "\xab\xcd");
// ["a" => "abcd", "b" => 171], expected the "Type C: not enough input values" warning and false, like "a*a/Cb"

unpack("Ca/H*b/X/Cc", "\x01\xab\xcd");
// "c" => 1 (re-reads byte 0), expected 205 (the last byte)

The fix sets size to the number of input bytes the nibbles came from, (len + 1) / 2. For explicit counts that's the value it already had, so only the * case changes.

The new phpt fails without the fix, and ext/standard/tests/strings passes with it.

Targeting PHP-8.4 as the lowest branch that still gets bug fixes. PHP-8.5 and up have the same bug, but the surrounding lines differ, so the merge-up needs the same line added by hand.

For the '*' repeater, the h/H case leaves size at -1. Unlike the a/A/Z
cases, which set size to the number of bytes they consumed, the hex case
never updates it, so "inputpos += size" moved the position one byte
backwards instead of to the end of the input. Any code following h* or
H* therefore re-read input that had already been consumed:

    unpack("H*a/H*b", "\xab\xcd");  // b => "abcd" instead of ""
    unpack("H*a/Cb", "\xab\xcd");   // b => 171 instead of a warning

Set size to the number of input bytes consumed by the nibbles that were
read. This is what was already used for explicit counts, so only the '*'
case changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant