Vulnerability: An issue was discovered in mruby 1.4.1. There is a heap-based buffer over-read associated with OP_ENTER because mrbgems/mruby-fiber/src/fiber.c does not extend the stack in cases of many arguments to fiber.
Vulnerability ID: CVE-2018-12248
CWE: CWE-125
Severity score: 2.9
Project: https://github.com/mruby/mruby
Published: 2018-06-12
File: mrbgems/mruby-fiber/src/fiber.c
Commit message: Extend stack when pushing arguments that does not fit in; fix #4038
Commit author: Yukihiro "Matz" Matsumoto

Vulnerable version (before fix):
 fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr {   struct mrb_context *c = fiber_check(mrb, self);   struct mrb_context *old_c = mrb->c;   mrb_value value;    fiber_check_cfunc(mrb, c);   if (resume && c->status == MRB_FIBER_TRANSFERRED) {     mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");   }   if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {     mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");   }   if (c->status == MRB_FIBER_TERMINATED) {     mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");   }   mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;   c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);   if (c->status == MRB_FIBER_CREATED) {     mrb_value *b, *e;      if (len >= c->stend - c->stack) {       mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");     }     b = c->stack+1;     e = b + len;     while (b<e) { fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr   else {     value = fiber_result(mrb, a, len);   }   fiber_switch_context(mrb, c);    if (vmexec) {     c->vmexec = TRUE;

Fixed version (after fix):
 fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr {   struct mrb_context *c = fiber_check(mrb, self);   struct mrb_context *old_c = mrb->c;   enum mrb_fiber_state status;   mrb_value value;    fiber_check_cfunc(mrb, c);   status = c->status;   if (resume && status == MRB_FIBER_TRANSFERRED) {     mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");   }   if (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) {     mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");   }   if (status == MRB_FIBER_TERMINATED) {     mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");   }   old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;   c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);   fiber_switch_context(mrb, c);   if (status == MRB_FIBER_CREATED) {     mrb_value *b, *e;      mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */     b = c->stack+1;     e = b + len;     while (b<e) { fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr   else {     value = fiber_result(mrb, a, len);   }    if (vmexec) {     c->vmexec = TRUE;

Patch:
patch: @@ -184,26 +184,27 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
 {
   struct mrb_context *c = fiber_check(mrb, self);
   struct mrb_context *old_c = mrb->c;
+  enum mrb_fiber_state status;
   mrb_value value;
 
   fiber_check_cfunc(mrb, c);
-  if (resume && c->status == MRB_FIBER_TRANSFERRED) {
+  status = c->status;
+  if (resume && status == MRB_FIBER_TRANSFERRED) {
     mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
   }
-  if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
+  if (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) {
     mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
   }
-  if (c->status == MRB_FIBER_TERMINATED) {
+  if (status == MRB_FIBER_TERMINATED) {
     mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
   }
-  mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
+  old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
   c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
-  if (c->status == MRB_FIBER_CREATED) {
+  fiber_switch_context(mrb, c);
+  if (status == MRB_FIBER_CREATED) {
     mrb_value *b, *e;
 
-    if (len >= c->stend - c->stack) {
-      mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");
-    }
+    mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
     b = c->stack+1;
     e = b + len;
     while (b<e) {
@@ -215,7 +216,6 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
   else {
     value = fiber_result(mrb, a, len);
   }
-  fiber_switch_context(mrb, c);
 
   if (vmexec) {
     c->vmexec = TRUE;
path: mrbgems/mruby-fiber/src/fiber.c