compile: double free of pc->fail_pop in codegen_pattern_or on allocation failure with nested or-pattern

Đang mở
#160,789 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
55/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
compilers

Hướng nghiên cứu

Start in Python/codegen.c at codegen_pattern_or and codegen_match, using the reported lines 6573 and 6775 to trace ownership of fail_pop through the allocation failure path. Reproduce with the provided _testcapi.call_with_nomemory script on an ASan build; done when injected failures raise MemoryError, the pointer is freed once, and the script prints “survived.”

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

interpreter-core type-crash

When an allocation fails while compiling a match statement whose or-pattern contains another or-pattern, codegen_pattern_or frees pc->fail_pop and codegen_match then frees it again, so compile() crashes instead of raising MemoryError.

The failure is injected with the test-only _testcapi.call_with_nomemory:

import ast, _testcapi
tree = ast.parse('def f(x):\n match x:\n  case [1|2] | 3: pass\n')
for off in range(1500):
    held = None
    held = [[] for _ in range(128)]   # refill/empty the list freelist so PyList_New in codegen really allocates
    try:
        _testcapi.call_with_nomemory(off, off + 1, compile, tree, 'a.py', 'exec')
    except (MemoryError, SyntaxError):
        pass
print('survived')

Saved as input.py and run on an ASan build that includes _testcapi:

python input.py

Output:

ERROR: AddressSanitizer: heap-use-after-free on address 0x503000002988
READ of size 1 at 0x503000002988 thread T0
    #0 in _PyMem_DebugCheckAddress Objects/obmalloc.c:3351
    #4 in PyMem_Free Objects/obmalloc.c:1292
    #5 in codegen_match Python/codegen.c:6775
...
freed by thread T0 here:
    #5 in PyMem_Free Objects/obmalloc.c:1292
    #6 in codegen_pattern_or Python/codegen.c:6573

Every injected failure should surface as MemoryError from compile(), with the pointer freed once, and the script should print survived.

Found at commit 3646ea571f3d8290806a3029d555b179f8be1539. It still reproduces at 3acc941f2b72 (latest upstream checked): ASan heap-use-after-free in the checking build, and the release build segfaults (exit code 139). case (1|2) | 3, where the inner or-pattern is not inside a sequence pattern, survives.

Looks like in the inner codegen_pattern_or, old_pc = *pc copies the outer fail_pop array, and if the first PyList_New(0) in the loop fails, goto error runs before pc->fail_pop = NULL. The error block then frees old_pc.fail_pop while pc->fail_pop still points to it, and codegen_match frees it again.

Found with an LLM-based testing tool; I used Claude to reduce it and look for the cause.

Linked PRs
  • gh-160875
Ngôn ngữ chính
Python
Star
77.8k
Fork
37.7k
Merge trung bình
17 giờ 24 phút
Pull request đã merge (30 ngày)
716

Chuẩn bị môi trường

Mở trong Codespaces

Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của python/cpython

Tất cả issue của python/cpython

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.