Skip to content

mruby-sprintf: protect format string from mutation during callbacks#6781

Merged
matz merged 1 commit into
masterfrom
fix/sprintf-uaf
Apr 11, 2026
Merged

mruby-sprintf: protect format string from mutation during callbacks#6781
matz merged 1 commit into
masterfrom
fix/sprintf-uaf

Conversation

@matz
Copy link
Copy Markdown
Member

@matz matz commented Apr 10, 2026

Summary

mrb_str_format captured raw C pointers (p, end) into the format string's buffer before the main loop. The %s and %p specifiers call to_s and inspect, which can invoke Ruby code that mutates the format string via String#replace, freeing or reallocating its buffer. The loop then continued iterating with dangling pointers, reading freed memory and potentially leaking adjacent heap contents into the result string.

Fix

Duplicate the format string with mrb_str_dup() before the loop. This is O(1) because mrb_str_dup shares the underlying buffer; if the original is later mutated via String#replace, str_replace decrements the shared refcount, leaving our duplicate's buffer intact.

Test plan

  • rake CONFIG=host-debug all test:run:serial — 1810 lib + 100 bintest OK
  • Regression test added: to_s callback calling $fmt.replace("Z") no longer corrupts sprintf output

mrb_str_format captured raw C pointers (p, end) into the format
string's buffer before the main loop. The %s and %p specifiers call
to_s and inspect, which can invoke Ruby code that mutates the format
string via String#replace, freeing or reallocating its buffer. The
loop then continued iterating with dangling pointers, reading freed
memory and potentially leaking adjacent heap contents into the result.

Duplicate the format string with mrb_str_dup() before the loop. This
is O(1) because mrb_str_dup shares the underlying buffer; if the
original is later mutated via String#replace, str_replace decrements
the shared refcount, leaving our duplicate's buffer intact.

Co-authored-by: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses a potential memory safety issue in mrb_str_format where callbacks could mutate the format string and invalidate internal pointers. It introduces a string duplication step to ensure the format string remains stable during processing and adds a corresponding test case to verify the fix. I have no feedback to provide.

@matz matz merged commit 48fc422 into master Apr 11, 2026
32 checks passed
@matz matz deleted the fix/sprintf-uaf branch April 11, 2026 07:33
matz added a commit that referenced this pull request Apr 12, 2026
Co-authored-by: Claude <noreply@anthropic.com>
dearblue added a commit to dearblue/mruby that referenced this pull request Apr 13, 2026
`mrb_str_dup()` always duplicates string objects in an unfrozen state, and the class is also set.
Therefore, it can be observed and modified from the Ruby side using the `ObjectSpace.each_object` method.

By using `mrb_str_dup_frozen()`, unnecessary duplication can be avoided, and modifications to the string can also be prevented.
dearblue added a commit to dearblue/mruby that referenced this pull request Apr 13, 2026
`mrb_str_dup()` always duplicates string objects in an unfrozen state, and the class is also set.
Therefore, it can be observed and modified from the Ruby side using the `ObjectSpace.each_object` method.

By using `mrb_str_dup_frozen()`, unnecessary duplication can be avoided, and modifications to the string can also be prevented.
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.

1 participant