GitHub

Original file line numberDiff line numberDiff line change

@@ -2201,15 +2201,27 @@ str_prepend(mrb_state *mrb, mrb_value self)

22012201
22022202

char *p = RSTRING_PTR(self);

22032203
2204-

/* Move original content to the end */

2204+

/* Move original content to the end. The original self data now lives

2205+

at p + total_prepend_len, which we use as the source for any

2206+

self-referencing arguments (e.g., s.prepend(s, s)) to avoid reading

2207+

data that has already been overwritten by earlier copies. */

22052208

memmove(p + total_prepend_len, p, self_len);

22062209
22072210

/* Copy prepended strings in order */

22082211

mrb_int offset = 0;

22092212

for (mrb_int i = 0; i < argc; i++) {

2210-

mrb_int arg_len = RSTRING_LEN(argv[i]);

2213+

const char *src;

2214+

mrb_int arg_len;

2215+

if (mrb_obj_eq(mrb, self, argv[i])) {

2216+

src = p + total_prepend_len;

2217+

arg_len = self_len;

2218+

}

2219+

else {

2220+

src = RSTRING_PTR(argv[i]);

2221+

arg_len = RSTRING_LEN(argv[i]);

2222+

}

22112223

if (arg_len > 0) {

2212-

memcpy(p + offset, RSTRING_PTR(argv[i]), arg_len);

2224+

memcpy(p + offset, src, arg_len);

22132225

offset += arg_len;

22142226

}

22152227

}

Original file line numberDiff line numberDiff line change

@@ -492,6 +492,21 @@ def assert_upto(exp, receiver, *args)

492492

g = "world"

493493

assert_equal "hello world", g.prepend("", "hello ", "")

494494

assert_equal "hello world", g

495+
496+

# Self-referencing arguments (GHSA-3hgj-g76g-878c)

497+

h = "A" * 100

498+

h.prepend(h, h)

499+

assert_equal 300, h.length

500+

assert_equal "A" * 300, h

501+
502+

# Mixed self-reference and literal

503+

i = "AB"

504+

i.prepend("XYZ", i)

505+

assert_equal "XYZABAB", i

506+
507+

j = "AB"

508+

j.prepend(j, "X", j)

509+

assert_equal "ABXABAB", j

495510

end

496511
497512

assert('String#ljust') do

Read the original on github.com ↗