CVE-2022-1071: Use After Free (CWE-416)
value: User after free in mrb_vm_exec in GitHub repository mruby/mruby prior to 3.2.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 8.2
Published: 2022-03-26T04:15Z
Repository: https://github.com/mruby/mruby
Language: C
Files changed: src/vm.c
Commit date: 2022-03-25 12:54:45 +0900
Commit message: vm.c: stack may be reallocated in functions calls.

Probably due to recursive VM calls via `mrb_funcall()`.

Diff: diff --git a/src/vm.c b/src/vm.c
index 8b81031a37b..fd17e90cc41 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -1392,18 +1392,20 @@ RETRY_TRY_BLOCK:
       case MRB_TT_ARRAY:
         if (!mrb_integer_p(vb)) goto getidx_fallback;
         regs[a] = mrb_ary_entry(va, mrb_integer(vb));
         break;
       case MRB_TT_HASH:
-        regs[a] = mrb_hash_get(mrb, va, vb);
+        va = mrb_hash_get(mrb, va, vb);
+        regs[a] = va;
         break;
       case MRB_TT_STRING:
         switch (mrb_type(vb)) {
         case MRB_TT_INTEGER:
         case MRB_TT_STRING:
         case MRB_TT_RANGE:
-          regs[a] = mrb_str_aref(mrb, va, vb, mrb_undef_value());
+          va = mrb_str_aref(mrb, va, vb, mrb_undef_value());
+          regs[a] = va;
           break;
         default:
           goto getidx_fallback;
         }
         break;
@@ -1421,21 +1423,23 @@ RETRY_TRY_BLOCK:
       SET_NIL_VALUE(regs[a+3]);
       goto L_SENDB_SYM;
     }
 
     CASE(OP_GETCONST, BB) {
-      regs[a] = mrb_vm_const_get(mrb, syms[b]);
+      mrb_value v = mrb_vm_const_get(mrb, syms[b]);
+      regs[a] = v;
       NEXT;
     }
 
     CASE(OP_SETCONST, BB) {
       mrb_vm_const_set(mrb, syms[b], regs[a]);
       NEXT;
     }
 
     CASE(OP_GETMCNST, BB) {
-      regs[a] = mrb_const_get(mrb, regs[a], syms[b]);
+      mrb_value v = mrb_const_get(mrb, regs[a], syms[b]);
+      regs[a] = v;
       NEXT;
     }
 
     CASE(OP_SETMCNST, BB) {
       mrb_const_set(mrb, regs[a+1], syms[b], regs[a]);
@@ -2012,18 +2016,19 @@ RETRY_TRY_BLOCK:
     }
 
     CASE(OP_KARG, BB) {
       mrb_value k = mrb_symbol_value(syms[b]);
       mrb_int kidx = mrb_ci_kidx(mrb->c->ci);
-      mrb_value kdict;
+      mrb_value kdict, v;
 
       if (kidx < 0 || !mrb_hash_p(kdict=regs[kidx]) || !mrb_hash_key_p(mrb, kdict, k)) {
         mrb_value str = mrb_format(mrb, "missing keyword: %v", k);
         mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
         goto L_RAISE;
       }
-      regs[a] = mrb_hash_get(mrb, kdict, k);
+      v = mrb_hash_get(mrb, kdict, k);
+      regs[a] = v;
       mrb_hash_delete_key(mrb, kdict, k);
       NEXT;
     }
 
     CASE(OP_KEY_P, BB) {

Vulnerable code:         regs[a] = mrb_hash_get(mrb, va, vb);
          regs[a] = mrb_str_aref(mrb, va, vb, mrb_undef_value());
      regs[a] = mrb_vm_const_get(mrb, syms[b]);
      regs[a] = mrb_const_get(mrb, regs[a], syms[b]);
      mrb_value kdict;
      regs[a] = mrb_hash_get(mrb, kdict, k);
Fixed code:         va = mrb_hash_get(mrb, va, vb);
        regs[a] = va;
          va = mrb_str_aref(mrb, va, vb, mrb_undef_value());
          regs[a] = va;
      mrb_value v = mrb_vm_const_get(mrb, syms[b]);
      regs[a] = v;
      mrb_value v = mrb_const_get(mrb, regs[a], syms[b]);
      regs[a] = v;
      mrb_value kdict, v;
      v = mrb_hash_get(mrb, kdict, k);
      regs[a] = v;