Skip to content

mruby-regexp: refuse a broken subject at the entry to split - #7133

Merged
matz merged 1 commit into
mruby:masterfrom
takumin:regexp-refuse-broken-utf8-split
Aug 13, 2026
Merged

mruby-regexp: refuse a broken subject at the entry to split#7133
matz merged 1 commit into
mruby:masterfrom
takumin:regexp-refuse-broken-utf8-split

Conversation

@takumin

@takumin takumin commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Builds on #7126 and #7127, which refuse a search whose subject holds a byte that spells no character and exempt the literal a quoted String pattern is searched for. split is what they left: CRuby refuses it whatever the pattern, and here a String or nil pattern answers.

"あ\x80b".split("b")  # CRuby: ArgumentError, before this: ["あ\x80"]
"あ\x80b".split       # CRuby: ArgumentError, before this: ["あ\x80b"]
"あ\x80b".split(" ")  # CRuby: ArgumentError, before this: ["あ\x80b"]

String#split hands those three to core's split, which this gem keeps under __split, so the path reaches no search of the gem's and the check every other call runs never fires. The refusal goes at the entry instead, through a Regexp.__check_encoding class method that is re_check_encoding() under a name mrblib can call.

On the name

#7110 carried a class method of this name to run the check once at the entry to each mrblib loop rather than once per match. It went away when the flag core leaves on a string it has read made the two placements measure the same. What brings it back is a path no search covers, not what a search costs.

What stays as it is

byteindex with a String pattern, and gsub with a block over one, answer a broken subject in CRuby too, so they keep the exemption #7127 gives them. Their Regexp forms already refuse.

A limit of 1 hands the subject back whole without reading it, whatever the pattern, and CRuby answers there too, so the check waits behind it. The limit is converted before either way: "あ\x80b".split("b", "x") raises TypeError in both.

A binary subject goes through as it did, since mrb_str_valid_encoding_p() takes a byte-indexed string as valid whatever its bytes are.

Checked

  • rake test on the default gembox: 2040 assertions, 0 failures; bintest 105, 0 failures
  • rake test on a full-core build (MRB_UTF8_STRING): 2259 assertions, 0 failures
  • rake test on a build without MRB_UTF8_STRING, holding mruby-regexp and mruby-string-ext: 1037 assertions, 0 failures
  • every split form above compared against CRuby 4.0.6, which agrees with all of them

Summary by CodeRabbit

  • Bug Fixes
    • Improved String#split handling for invalid UTF-8 strings.
    • Splitting invalid UTF-8 content now raises ArgumentError when applicable, while limit == 1 preserves the original string.
    • Invalid, non-integer split limits now raise TypeError consistently.
    • Preserved regular-expression splitting behavior for byte-oriented strings.

The two commits before this refuse a search whose subject holds a byte that
spells no character, and exempt the literal a quoted String pattern is searched
for. `split` is what they left: CRuby refuses it whatever the pattern, and here
a String or nil pattern answered.

```ruby
"あ\x80b".split("b")  # CRuby: ArgumentError, before this: ["あ\x80"]
"あ\x80b".split       # CRuby: ArgumentError, before this: ["あ\x80b"]
"あ\x80b".split(" ")  # CRuby: ArgumentError, before this: ["あ\x80b"]
```

`String#split` hands those three to core's `split`, which this gem keeps under
`__split`, so the path reaches no search of the gem's and the check every other
call runs never fires. The refusal goes at the entry instead, through a
`Regexp.__check_encoding` class method that is `re_check_encoding()` under a
name mrblib can call.

That name was in mruby#7110 for a different reason: to run the check once at the
entry to each mrblib loop rather than once per match. It went away when the
flag core leaves on a string it has read made the two placements measure the
same. What brings it back is a path no search covers, not what a search costs.

A limit of 1 hands the subject back whole without reading it, whatever the
pattern, and CRuby answers there too, so the check waits behind it. The limit
is converted before either way: `"あ\x80b".split("b", "x")` raises `TypeError`
in both.

A binary subject goes through as it did, since `mrb_str_valid_encoding_p()`
takes a byte-indexed string as valid whatever its bytes are, and a build
without `MRB_UTF8_STRING` reads no encoding for them to break.
@takumin
takumin requested a review from matz as a code owner August 13, 2026 00:39
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 63a01f69-95d0-4937-aa32-1a0de57cdfa0

📥 Commits

Reviewing files that changed from the base of the PR and between 0c0c311 and c26c812.

📒 Files selected for processing (3)
  • mrbgems/mruby-regexp/mrblib/string_regexp.rb
  • mrbgems/mruby-regexp/src/regexp.c
  • mrbgems/mruby-regexp/test/regexp_utf8.rb

📝 Walkthrough

Walkthrough

String#split now validates invalid UTF-8 subjects for applicable patterns and limits. Regexp.__check_encoding performs the validation. Tests cover limit handling, type errors, and byte-indexed subjects.

Changes

UTF-8 split validation

Layer / File(s) Summary
Encoding check helper
mrbgems/mruby-regexp/src/regexp.c
Regexp.__check_encoding validates String arguments with re_check_encoding and is registered as an internal class method.
Split integration and tests
mrbgems/mruby-regexp/mrblib/string_regexp.rb, mrbgems/mruby-regexp/test/regexp_utf8.rb
String#split checks receiver encoding for nil and String patterns unless limit == 1. Tests cover invalid UTF-8, limit types, and byte-oriented splitting.

Estimated code review effort: 2 (Simple) | ~10 minutes

Mergeability Score: ⚪ Minimal · up to c26c8

This localized change makes split reject invalid UTF-8 subjects consistently with CRuby, with no actionable merge-blocking risk remaining after normal checks and review.

Possibly related PRs

  • mruby/mruby#7110: Both changes modify Regexp.__check_encoding and String#split UTF-8 validation.
  • mruby/mruby#7126: Both changes extend UTF-8 subject validation for String#split.
  • mruby/mruby#7116: Both changes expand UTF-8 and byte-indexed subject tests.

Suggested reviewers: matz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: rejecting invalid subject encoding at the entry to String#split.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@matz
matz merged commit 3adb9e4 into mruby:master Aug 13, 2026
21 checks passed
@takumin
takumin deleted the regexp-refuse-broken-utf8-split branch August 13, 2026 03:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants