Skip to content

fix: t:utf8toUnicode drops the last hex digit of code points above U+FFFF - #3685

Merged
airween merged 2 commits into
owasp-modsecurity:v2/masterfrom
fzipi:v2/issue-3684-utf8tounicode-truncation
Oct 15, 2026
Merged

airween merged 2 commits into
owasp-modsecurity:v2/masterfrom
fzipi:v2/issue-3684-utf8tounicode-truncation

Conversation

@fzipi

@fzipi fzipi commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

Proposed changes

Fixes #3684. Related to #1211 (the 4-vs-6 digit output ambiguity, which this PR does not change).

Since the refactor in #3284 (first released in 2.9.9) utf8_unicode_inplace_ex() always advanced the output pointer by six bytes after sprintf("%%u%04x"), so any code point above U+FFFF (five hex digits) lost its last digit to the next write or to the terminating NUL: U+1F600 came out as %u1f60. The pointer now advances by what sprintf() actually wrote.

Two adjacent checks in the same function are fixed as well:

  • bytes_left was never decremented, so the "enough bytes left" guards compared against the total input length instead of the remaining length, and a truncated sequence at the end of the value was examined past input_len (bounded in practice by the NUL that terminates variable values, but wrong).
  • An 0xF4 lead byte with a second byte >= 0x90 (U+110000 and above, outside Unicode) was accepted as a valid four-byte sequence; it is now passed through like any other invalid sequence.

Output is otherwise unchanged, including the pre-existing handling of a NUL byte (%u00xx from #3284) and of the BMP.

Tests

tests/tfn/utf8toUnicode.t had no four-byte input at all. Four cases are added: supplementary-plane characters, a four-byte character followed by ASCII, a sequence above U+10FFFF, and a truncated sequence at the end of the input. Against the unmodified v2/master source the first three fail (Passed: 8; Failed: 3); with this patch all 11 pass and make test is green (macOS, Apache 2.4 from Homebrew, PCRE2 10.49). The function was also exercised standalone under AddressSanitizer with the same inputs.

🤖 Generated with Claude Code

…FFFF

Since the refactor in owasp-modsecurity#3284 (2.9.9) the output pointer always advanced
by six bytes after sprintf("%%u%04x"), so a code point that needs five
hex digits (anything above U+FFFF) lost its last digit to the next write
or to the terminating NUL: U+1F600 came out as "%u1f60". Advance by the
number of bytes sprintf actually wrote.

While there, fix two adjacent checks in the same function: bytes_left
was never decremented, so the "enough bytes left" guards compared the
total length instead of the remaining length and a truncated sequence at
the end of the value was examined past input_len; and an 0xF4 lead byte
with a second byte >= 0x90 (U+110000 and above, outside Unicode) was
accepted as a valid four-byte sequence.

Add four-byte, above-U+10FFFF and truncated-sequence cases to the
utf8toUnicode unit tests, which had no four-byte input at all.

Fixes owasp-modsecurity#3684

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cq9GqRpB4wzTFnEUvQaUJg
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 169bbd4a-65b0-4510-a7d0-b8a9eb02a4b4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread apache2/msc_util.c Fixed
Sonar flagged the sprintf call added by the previous commit. The
output buffer is sized for the worst case, so this only makes the
bound explicit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mhspv9mxtMSiWC1U8mVH1H
@sonarqubecloud

sonarqubecloud Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Quality Gate Passed Quality Gate passed

Issues
0 New issues
1 Accepted issue

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

fzipi added a commit to fzipi/ModSecurity that referenced this pull request Oct 15, 2026
…e_ex

The two-, three- and four-byte branches repeated the same pattern: check
the byte count, check each continuation byte for binary 10, shift in six
bits. Move that into utf8_decode_sequence(), which derives the sequence
length from the lead byte and loops over the continuation bytes. The NUL
special case and the surrogate, range and overlong checks stay in the
caller, so the output is unchanged.

SonarCloud reported a cognitive complexity of 51 for the function on
PR owasp-modsecurity#3685 (accepted there); it is now 17, with 10 for the helper.

Verified output-identical to the previous version with a standalone
harness under ASan and UBSan: every input of 1 to 3 bytes, every 4-byte
input with a high lead byte and a third of the fourth-byte values, and
20 million random 4 to 15 byte inputs biased to high bytes and NUL:
758,263,296 cases, 0 mismatches. tests/tfn/utf8toUnicode.t passes 11/11.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tz1sULBvqAwnRreKh5eC9h
@airween airween added the 2.x Related to ModSecurity version 2.x label Oct 15, 2026

@airween airween left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thanks!

@airween
airween merged commit 99b228c into owasp-modsecurity:v2/master Oct 15, 2026
93 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2.x Related to ModSecurity version 2.x

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants