]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-94438: Account for NULLs on evaluation stack when jumping lines. (GH-94444)
authorMark Shannon <mark@hotpy.org>
Fri, 1 Jul 2022 13:01:14 +0000 (14:01 +0100)
committerGitHub <noreply@github.com>
Fri, 1 Jul 2022 13:01:14 +0000 (14:01 +0100)
Lib/test/test_sys_settrace.py
Misc/NEWS.d/next/Core and Builtins/2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst [new file with mode: 0644]
Objects/frameobject.c

index 162fd8328582ca8bb5f26a70570d967613f9fd68..fd2740ea19f0c2ea0410f23c05ee458aea4f508d 100644 (file)
@@ -2264,25 +2264,25 @@ class JumpTestCase(unittest.TestCase):
             output.append(2)
         output.append(3)
 
-    @jump_test(1, 3, [], (ValueError, 'depth'))
+    @jump_test(1, 3, [], (ValueError, 'stack'))
     def test_no_jump_forwards_into_with_block(output):
         output.append(1)
         with tracecontext(output, 2):
             output.append(3)
 
-    @async_jump_test(1, 3, [], (ValueError, 'depth'))
+    @async_jump_test(1, 3, [], (ValueError, 'stack'))
     async def test_no_jump_forwards_into_async_with_block(output):
         output.append(1)
         async with asynctracecontext(output, 2):
             output.append(3)
 
-    @jump_test(3, 2, [1, 2, -1], (ValueError, 'depth'))
+    @jump_test(3, 2, [1, 2, -1], (ValueError, 'stack'))
     def test_no_jump_backwards_into_with_block(output):
         with tracecontext(output, 1):
             output.append(2)
         output.append(3)
 
-    @async_jump_test(3, 2, [1, 2, -1], (ValueError, 'depth'))
+    @async_jump_test(3, 2, [1, 2, -1], (ValueError, 'stack'))
     async def test_no_jump_backwards_into_async_with_block(output):
         async with asynctracecontext(output, 1):
             output.append(2)
@@ -2584,6 +2584,63 @@ output.append(4)
         output.append(7)
         output.append(8)
 
+    # checking for segfaults.
+    @jump_test(3, 7, [], error=(ValueError, "stack"))
+    def test_jump_with_null_on_stack_load_global(output):
+        a = 1
+        print(
+            output.append(3)
+        )
+        output.append(5)
+        (
+            ( # 7
+                a
+                +
+                10
+            )
+            +
+            13
+        )
+        output.append(15)
+
+    # checking for segfaults.
+    @jump_test(4, 8, [], error=(ValueError, "stack"))
+    def test_jump_with_null_on_stack_push_null(output):
+        a = 1
+        f = print
+        f(
+            output.append(4)
+        )
+        output.append(6)
+        (
+            ( # 8
+                a
+                +
+                11
+            )
+            +
+            14
+        )
+        output.append(16)
+
+    # checking for segfaults.
+    @jump_test(3, 7, [], error=(ValueError, "stack"))
+    def test_jump_with_null_on_stack_load_attr(output):
+        a = 1
+        list.append(
+            output, 3
+        )
+        output.append(5)
+        (
+            ( # 7
+                a
+                +
+                10
+            )
+            +
+            13
+        )
+        output.append(15)
 
 class TestExtendedArgs(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst
new file mode 100644 (file)
index 0000000..b08dd8f
--- /dev/null
@@ -0,0 +1,2 @@
+Account for instructions that can push NULL to the stack when setting line
+number in a frame. Prevents some (unlikely) crashes.
index 44cc06205a623775dda375f69612642e561a3354..34a6c46c6b57f2a70e986aaaa68523f6f977d6f5 100644 (file)
@@ -137,9 +137,24 @@ typedef enum kind {
     Iterator = 1,
     Except = 2,
     Object = 3,
+    Null = 4,
 } Kind;
 
-#define BITS_PER_BLOCK 2
+static int
+compatible_kind(Kind from, Kind to) {
+    if (to == 0) {
+        return 0;
+    }
+    if (to == Object) {
+        return from != Null;
+    }
+    if (to == Null) {
+        return 1;
+    }
+    return from == to;
+}
+
+#define BITS_PER_BLOCK 3
 
 #define UNINITIALIZED -2
 #define OVERFLOWED -1
@@ -298,6 +313,31 @@ mark_stacks(PyCodeObject *code_obj, int len)
                 case RERAISE:
                     /* End of block */
                     break;
+                case PUSH_NULL:
+                    next_stack = push_value(next_stack, Null);
+                    stacks[i+1] = next_stack;
+                    break;
+                case LOAD_GLOBAL:
+                {
+                    int j = get_arg(code, i);
+                    if (j & 1) {
+                        next_stack = push_value(next_stack, Null);
+                    }
+                    next_stack = push_value(next_stack, Object);
+                    stacks[i+1] = next_stack;
+                    break;
+                }
+                case LOAD_ATTR:
+                {
+                    int j = get_arg(code, i);
+                    if (j & 1) {
+                        next_stack = pop_value(next_stack);
+                        next_stack = push_value(next_stack, Null);
+                        next_stack = push_value(next_stack, Object);
+                    }
+                    stacks[i+1] = next_stack;
+                    break;
+                }
                 default:
                 {
                     int delta = PyCompile_OpcodeStackEffect(opcode, _Py_OPARG(code[i]));
@@ -318,17 +358,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
     return stacks;
 }
 
-static int
-compatible_kind(Kind from, Kind to) {
-    if (to == 0) {
-        return 0;
-    }
-    if (to == Object) {
-        return 1;
-    }
-    return from == to;
-}
-
 static int
 compatible_stack(int64_t from_stack, int64_t to_stack)
 {
@@ -365,7 +394,8 @@ explain_incompatible_stack(int64_t to_stack)
         case Except:
             return "can't jump into an 'except' block as there's no exception";
         case Object:
-            return "differing stack depth";
+        case Null:
+            return "incompatible stacks";
         case Iterator:
             return "can't jump into the body of a for loop";
         default: