Skip to content

Commit 48fc422

Browse files
authored
Merge pull request mruby#6781 from mruby/fix/sprintf-uaf
2 parents 18ba026 + 59552ec commit 48fc422

File tree

2 files changed

+23
-0
lines changed

2 files changed

+23
-0
lines changed

mrbgems/mruby-sprintf/src/sprintf.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,13 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
380380
argc++;
381381
argv--;
382382
mrb_ensure_string_type(mrb, fmt);
383+
/* Duplicate the format string so that to_s/inspect callbacks invoked
384+
during the loop cannot invalidate p/end by mutating the original
385+
via String#replace or similar. mrb_str_dup shares the underlying
386+
buffer, so this is O(1); String#replace on the original goes
387+
through str_replace which decrements the shared refcount, leaving
388+
our copy's buffer intact. */
389+
fmt = mrb_str_dup(mrb, fmt);
383390
p = RSTRING_PTR(fmt);
384391
end = p + RSTRING_LEN(fmt);
385392
blen = 0;

mrbgems/mruby-sprintf/test/sprintf.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,19 @@
9090
"%?" % ""
9191
end
9292
end
93+
94+
assert("sprintf with to_s mutating format string") do
95+
# The to_s callback must not be able to invalidate sprintf's internal
96+
# iteration pointers by mutating the format string.
97+
fmt = "%s" + "B" * 200
98+
mutator = Object.new
99+
$sprintf_test_fmt = fmt
100+
def mutator.to_s
101+
$sprintf_test_fmt.replace("Z")
102+
"ok"
103+
end
104+
result = sprintf(fmt, mutator)
105+
assert_equal 202, result.length
106+
assert_equal "ok", result[0, 2]
107+
assert_equal "B" * 200, result[2..]
108+
end

0 commit comments

Comments
 (0)