This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Check for PyErr_Occurred() after PyImport_GetModule().
Type: crash Stage: resolved
Components: Interpreter Core Versions: Python 3.8, Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: asmeurer, brett.cannon, eric.snow, ncoghlan, remi.lapeyre, serhiy.storchaka, skrah, xtreak
Priority: normal Keywords: patch

Created on 2019-03-19 19:01 by asmeurer, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 12504 merged skrah, 2019-03-22 23:42
PR 12539 merged miss-islington, 2019-03-25 20:52
Messages (13)
msg338399 - (view) Author: Aaron Meurer (asmeurer) Date: 2019-03-19 19:01
I am getting a Fatal Python error: Cannot recover from stack overflow. running the SymPy tests on a branch of mine where the tests fail. I have reproduced this in Python 3.6.7, as well as CPython master (fc96e5474a7bda1c5dec66420e4467fc9f7ca968).

Here are the repro steps:

1. Check out my git branch https://github.com/asmeurer/sympy/tree/python-crash
2. Install or point PYTHONPATH to mpmath
3. Run python and type

from sympy import test
test('sets', subprocess=False)

The tests will run (with failures) until they reach a point where Python crashes with

Fatal Python error: Cannot recover from stack overflow.

Current thread 0x00007fffa8e623c0 (most recent call first):
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/core/relational.py", line 385 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1594 in _contains
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 286 in contains
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1257 in <genexpr>
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/core/logic.py", line 139 in fuzzy_and
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1257 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 551 in __sub__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1269 in _handle_finite_sets
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1900 in simplify_intersection
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1200 in __new__
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 109 in intersect
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 309 in is_subset
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1348 in reduce
  File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/sets/sets.py", line 1338 in __new__
  ...
Abort trap: 6


Unfortunately, I'm not able to find a simpler reproducing example for CPython master, as it seems to be heavily dependent on the exact state of the call stack.

I also get a crash report from OS X if that's helpful:

Process:               python.exe [86693]
Path:                  /Users/USER/Documents/*/python.exe
Identifier:            python.exe
Version:               0
Code Type:             X86-64 (Native)
Parent Process:        bash [612]
Responsible:           python.exe [86693]
User ID:               501

Date/Time:             2019-03-19 13:00:28.968 -0600
OS Version:            Mac OS X 10.12.6 (16G1917)
Report Version:        12
Anonymous UUID:        5AC59B85-E5D2-1EA1-6881-7497830B3180

Sleep/Wake UUID:       44F9CEAE-2035-49EE-A6EF-02AB11D4F067

Time Awake Since Boot: 140000 seconds
Time Since Wake:       5200 seconds

System Integrity Protection: enabled

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Application Specific Information:
abort() called

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	0x00007fffa006ed42 __pthread_kill + 10
1   libsystem_pthread.dylib       	0x00007fffa015c457 pthread_kill + 90
2   libsystem_c.dylib             	0x00007fff9ffd4420 abort + 129
3   python.exe                    	0x000000010ba4f110 fatal_error + 64 (pylifecycle.c:1983)

Thread 0 crashed with X86 Thread State (64-bit):
  rax: 0x0000000000000000  rbx: 0x0000000000000006  rcx: 0x00007fff542a7c18  rdx: 0x0000000000000000
  rdi: 0x0000000000000307  rsi: 0x0000000000000006  rbp: 0x00007fff542a7c40  rsp: 0x00007fff542a7c18
   r8: 0x0000000000000040   r9: 0x00007fffa8e46040  r10: 0x0000000008000000  r11: 0x0000000000000206
  r12: 0x000000010bb10470  r13: 0x0000000000000000  r14: 0x00007fffa8e623c0  r15: 0x0000000000000000
  rip: 0x00007fffa006ed42  rfl: 0x0000000000000206  cr2: 0x00007fffa8e44128
  
Logical CPU:     0
Error Code:      0x02000148
Trap Number:     133
msg338435 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-03-20 02:42
From the stack trace it looks like some kind of recursion occurs on the tests causing the crash. Sympy is not a part of stdlib. Can you please add a pure Python reproducer or this is something that needs to be fixed by reporting to sympy repo.
msg338469 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2019-03-20 13:17
FWIW I just tried both f8e46e9e741f253803e9b8be03287e5dd16abd4d and f8e46e9e741f253803e9b8be03287e5dd16abd4d with the reproducer given and none of them segfault.

I don't think there is a bug in the Python interpreter, there is some ways to trigger this error when a RecursionError is not handled quickly enough. I suppose you just have a problem in Sympy. I also find suspicious that you reproducer change many lines in Sympy and would look at that first.
msg338475 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-20 14:32
This occurs when handling a recursion error uses more than 50 extra nested function calls:

if (tstate->overflowed) {
        if (tstate->recursion_depth > recursion_limit + 50) {
            /* Overflowing while handling an overflow. Give up. */
            Py_FatalError("Cannot recover from stack overflow.");
        }
        return 0;
    }



You can set the recursion limit with sys.setrecursionlimit(), but it is the extra stack depth that matters here.
msg338476 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-20 14:38
It can still be an issue in CPython, like in #14537.
msg338479 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-20 15:09
The tests pass here on Linux with 3.8 (cc60cdd9c4) and a very low sys.setrecursionlimit(150).

The fail properly with RecursionError at sys.setrecursionlimit(125).


So I guess we'd need a gdb stack trace from OS X in case there's a CPython issue that is OS X specific.
msg338483 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-20 15:26
Whoops, I tested the wrong branch, getting a proper abort() now. :)
msg338490 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-20 16:03
The issue is that PyImport_GetModule() can legitimately NULL (not found)
but also NULL after an error occurred in PyDict_GetItemWithError().

So one (quick and dirty) approach that fixes this abort() is:

diff --git a/Python/import.c b/Python/import.c
index bf3a99414f..22eecd7cd6 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -1735,7 +1735,10 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
     }
 
     mod = PyImport_GetModule(abs_name);
-    if (mod != NULL && mod != Py_None) {
+    if (mod == NULL && PyErr_Occurred()) {
+        goto error;
+    }
+    else if (mod != NULL && mod != Py_None) {
         _Py_IDENTIFIER(__spec__);
         _Py_IDENTIFIER(_lock_unlock_module);
         PyObject *spec;


cc Brett as the import expert, perhaps he would like another approach.
msg338496 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2019-03-20 16:56
Pulling in Eric and Nick as they have played w/ the C API more recently.
msg338498 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-20 17:10
Actually I just see that this behavior of PyImport_GetModule() is documented:

"Return the already imported module with the given name. If the module has not been imported yet then returns NULL but does not set an error. Returns NULL and sets an error if the lookup failed."

New in version 3.7.


So it should indeed be just a matter of always checking for PyErr_Occurred().
msg338822 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-25 20:51
New changeset 027b09c5a13aac9e14a3b43bb385298d549c3833 by Stefan Krah in branch 'master':
bpo-36370: Check for PyErr_Occurred() after PyImport_GetModule() (GH-12504)
https://github.com/python/cpython/commit/027b09c5a13aac9e14a3b43bb385298d549c3833
msg338823 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-25 21:36
New changeset cdd8d4d6dd57f4c9429566706009d4613277d391 by Stefan Krah (Miss Islington (bot)) in branch '3.7':
bpo-36370: Check for PyErr_Occurred() after PyImport_GetModule() (GH-12504)
https://github.com/python/cpython/commit/cdd8d4d6dd57f4c9429566706009d4613277d391
msg338824 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2019-03-25 21:40
It looks like 3.6 is in security-fix only mode, so it cannot be backported there.
History
Date User Action Args
2022-04-11 14:59:12adminsetgithub: 80551
2019-03-25 21:40:26skrahsetstatus: open -> closed
versions: + Python 3.7, - Python 3.6
messages: + msg338824

resolution: fixed
stage: patch review -> resolved
2019-03-25 21:36:48skrahsetmessages: + msg338823
2019-03-25 20:52:35miss-islingtonsetpull_requests: + pull_request12488
2019-03-25 20:51:04skrahsetmessages: + msg338822
2019-03-23 07:36:33serhiy.storchakasetnosy: + serhiy.storchaka
2019-03-22 23:42:42skrahsetkeywords: + patch
stage: needs patch -> patch review
pull_requests: + pull_request12454
2019-03-20 17:10:35skrahsetmessages: + msg338498
2019-03-20 16:56:18brett.cannonsetnosy: + ncoghlan, eric.snow
messages: + msg338496
2019-03-20 16:09:15skrahsetstage: needs patch
2019-03-20 16:03:41skrahsetnosy: + brett.cannon

messages: + msg338490
title: "Fatal Python error: Cannot recover from stack overflow" from SymPy tests -> Check for PyErr_Occurred() after PyImport_GetModule().
2019-03-20 15:26:10skrahsetmessages: + msg338483
2019-03-20 15:09:42skrahsetmessages: + msg338479
2019-03-20 14:38:42skrahsetmessages: + msg338476
2019-03-20 14:32:26skrahsetnosy: + skrah
messages: + msg338475
2019-03-20 13:17:58remi.lapeyresetnosy: + remi.lapeyre
messages: + msg338469
2019-03-20 02:42:21xtreaksetnosy: + xtreak
messages: + msg338435
2019-03-19 19:01:43asmeurercreate