Skip to content

Conversation

@lalinsky
Copy link

@lalinsky lalinsky commented Jan 2, 2026

When all 16 bytes match the allowed range, match_mask becomes 0 after the bitwise NOT. Calling __builtin_ctzll(0) is undefined behavior.

The code expects match_len == 16 when all bytes match (so the branch is skipped and p += 16 continues the loop), but this relied on ctzll(0) returning 64, which is not guaranteed.

Example panic on macOS ARM64:

thread 44856 panic: passing zero to ctz(), which is not a valid argument
src/llhttp/llhttp.c:2654:21: in llhttp__internal__run
        match_len = __builtin_ctzll(match_mask) >> 2;
                    ^

Fix by explicitly checking for match_mask == 0 and setting match_len = 16.

Related: lalinsky/dusty#14

When all 16 bytes match the allowed range, match_mask becomes 0 after
the bitwise NOT. Calling __builtin_ctzll(0) is undefined behavior.

The code expects match_len == 16 when all bytes match (so the branch
is skipped and p += 16 continues the loop), but this relied on
ctzll(0) returning 64, which is not guaranteed.

Example panic on macOS ARM64:

    thread 44856 panic: passing zero to ctz(), which is not a valid argument
    src/llhttp/llhttp.c:2654:21: in llhttp__internal__run
            match_len = __builtin_ctzll(match_mask) >> 2;
                        ^

Fix by explicitly checking for match_mask == 0 and setting match_len = 16.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant