Sfoglia il codice sorgente

fix some stack overflow error

blueloveTH 9 ore fa
parent
commit
f6ae7cac7e
3 ha cambiato i file con 38 aggiunte e 0 eliminazioni
  1. 9 0
      src/interpreter/ceval.c
  2. 7 0
      src/interpreter/vm.c
  3. 22 0
      tests/280_exception.py

+ 9 - 0
src/interpreter/ceval.c

@@ -1018,6 +1018,11 @@ __NEXT_STEP:
             py_TValue* p;
             int length;
 
+            if(SP() + byte.arg > self->stack.end) {
+                py_exception(tp_RecursionError, "value stack overflow");
+                goto __ERROR;
+            }
+
             switch(TOP()->type) {
                 case tp_tuple: {
                     length = py_tuple_len(TOP());
@@ -1083,6 +1088,10 @@ __NEXT_STEP:
             DISPATCH();
         }
         case OP_UNPACK_EX: {
+            if(SP() + byte.arg + 1 > self->stack.end) {
+                py_exception(tp_RecursionError, "value stack overflow");
+                goto __ERROR;
+            }
             py_TValue* p;
             int length = pk_arrayview(TOP(), &p);
             if(length == -1) {

+ 7 - 0
src/interpreter/vm.c

@@ -507,6 +507,13 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
         Function* fn = py_touserdata(p0);
         const CodeObject* co = &fn->decl->code;
 
+        // the callee's locals live on the value stack; make room before any of
+        // the paths below writes there
+        if(argv + co->nlocals > self->stack.end) {
+            py_exception(tp_RecursionError, "value stack overflow");
+            return RES_ERROR;
+        }
+
         switch(fn->decl->type) {
             case FuncType_NORMAL: {
                 bool ok = prepare_py_call(self->vectorcall_buffer, argv, p1, kwargc, fn->decl);

+ 22 - 0
tests/280_exception.py

@@ -353,3 +353,25 @@ try:
         exit(1)
 except TypeError:
     pass
+
+# A value stack overflow must raise, not corrupt memory. Recursion with many
+# locals exhausts the value stack well before the recursion-depth limit.
+def _deep(n):
+    a, b, c, d, e = 1, 2, 3, 4, 5
+    f, g, h, i, j = 1, 2, 3, 4, 5
+    k, l, m, o, p = 1, 2, 3, 4, 5
+    q, r, s, t, u = 1, 2, 3, 4, 5
+    if n == 0:
+        return a
+    return _deep(n - 1)
+
+try:
+    _deep(5000)
+    exit(1)
+except RecursionError:
+    pass
+
+# the VM has to stay usable afterwards
+assert sum(range(100)) == 4950
+assert [x * 2 for x in range(4)] == [0, 2, 4, 6]
+