Fix use-after-free in mrb_ary_delete()

`mrb_equal()` may call `obj.==` method internally.
Therefore, using an unupdated pointer and length after `mrb_equal()` could result in a read/write to an invalid address.

Fresh properties must always be obtained regardless of the result of `mrb_equal()`.
Also, `ary_modify()` must be called each time before writing.

ref. #6339
This commit is contained in:
dearblue
2024-09-13 21:44:56 +09:00
parent 6e44c0bc91
commit 0955539cf9
+8 -12
View File
@@ -1551,17 +1551,12 @@ mrb_ary_delete(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "o&", &obj, &blk);
struct RArray *ary = RARRAY(self);
mrb_value *val_ptr = ARY_PTR(ary);
size_t len = ARY_LEN(ary);
mrb_bool modified = FALSE;
mrb_value ret = obj;
int ai = mrb_gc_arena_save(mrb);
size_t i = 0;
size_t j = 0;
for (; i < len; i++) {
mrb_value elem = val_ptr[i];
for (; i < ARY_LEN(ary); i++) {
mrb_value elem = ARY_PTR(ary)[i];
if (mrb_equal(mrb, elem, obj)) {
mrb_gc_arena_restore(mrb, ai);
@@ -1571,12 +1566,13 @@ mrb_ary_delete(mrb_state *mrb, mrb_value self)
}
if (i != j) {
if (!modified) {
ary_modify(mrb, ary);
val_ptr = ARY_PTR(ary);
modified = TRUE;
if (j >= ARY_LEN(ary)) {
// Since breaking here will further change the array length,
// there is no choice but to raise an exception or return.
mrb_raise(mrb, E_RUNTIME_ERROR, "array modified during delete");
}
val_ptr[j] = elem;
ary_modify(mrb, ary);
ARY_PTR(ary)[j] = elem;
}
j++;