Skip to content

refactor: extract the UTF-8 sequence decoder from utf8_unicode_inplace_ex (2.x) - #3697

Open
fzipi wants to merge 3 commits into
owasp-modsecurity:v2/masterfrom
fzipi:v2/refactor-utf8-decoder
Open

fzipi wants to merge 3 commits into
owasp-modsecurity:v2/masterfrom
fzipi:v2/refactor-utf8-decoder

Conversation

@fzipi

@fzipi fzipi commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Proposed changes

Follow-up to #3685, where SonarCloud flagged utf8_unicode_inplace_ex() in apache2/msc_util.c for a cognitive complexity of 51 (limit 25). That was accepted on #3685 because it is a bug fix; this PR does the refactor separately so it can be reviewed as one.

The two-, three- and four-byte branches of the decoder repeated the same pattern: check the byte count, check each continuation byte for binary 10, shift in six bits. They move into a small static helper, 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. The error codes the old branches distinguished are never observed: the caller only tests unicode_len > 0, and the UNICODE_ERROR_* switch in re_operators.c belongs to the separate decoder behind @validateUtf8Encoding.

Cognitive complexity is now 17 for the function and 10 for the helper. The output is unchanged.

This PR is based on #3685's branch and shows its two commits until that one is merged; only the last commit is the refactor.

Tests

  • tests/tfn/utf8toUnicode.t, including the four cases added by fix: t:utf8toUnicode drops the last hex digit of code points above U+FFFF #3685: 11 of 11 pass (Ubuntu 24.04 container, Apache 2.4, PCRE2, --enable-assertions). The only failure in make check there is the pre-existing op "ipMatch" assertion on 10.0.0.0/100, unrelated to this file.
  • A standalone harness compiled the previous and the new function side by side under ASan and UBSan and compared the full output buffer and the changed flag: 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, no sanitizer reports.

🤖 Generated with Claude Code

fzipi and others added 2 commits October 13, 2026 18:45
…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
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
@coderabbitai

coderabbitai Bot commented Oct 15, 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: 4caf1317-cc7b-4ba9-88cd-dfb16a1a38bd

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.

…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
@fzipi
fzipi force-pushed the v2/refactor-utf8-decoder branch from c96990a to 04b4e47 Compare October 15, 2026 17:18
@sonarqubecloud

Copy link
Copy Markdown

@fzipi fzipi added 2.x Related to ModSecurity version 2.x refactor labels Oct 15, 2026

This branch has not been deployed

No deployments
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 refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant