HexBrain

shopify-scripts · Report #201897

Recursion causing uninitialized memory reads leading to a segfault

Validstatus
lowseverity
$2,000bounty
4upvotes
28 Feb 2017disclosed

How it was found

The following code produces a segfault without causing a stack overflow, affecting the sandbox:

def fn(n)
    return
    ensure
        if n == 0
    else fn(n-1)
    end
end
fn(24)

When the n parameter is less than 24, there is no segfault. However, investigating with a memory sanitizer shows that uninitialized read errors start to happen when n >= 15. The uninitialized read happens at the following location in vm.c (with the relevant line marked):

1701         cipop(mrb);
1702         acc = ci->acc;  [***]
1703         mrb->c->stack = ci->stackent;

The issue seems to be in using the ci data after the call to cipop. The following patch stops the read errors and segfaults, and passes make test:

diff --git a/src/vm.c b/src/vm.c
index 9684dab..66fb692 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -1698,9 +1698,10 @@ RETRY_TRY_BLOCK:
           mrb->jmp = prev_jmp;
           return v;
         }
-        cipop(mrb);
+        ci = mrb->c->ci;
         acc = ci->acc;
         mrb->c->stack = ci->stackent;
+        cipop(mrb);
         if (acc == CI_ACC_SKIP) {
           mrb->jmp = prev_jmp;
           return v;

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original