Repository navigation
Heisenbug that kills process via SIGALRM #133687
Description
Bug report
Bug description:
First of all, I know exactly how this bug is going to sound: batshit insane. I implore you to keep reading.
I've had a timer application for ages that uses signal.alarm() concurrently with input() so that it has a rudimentary way of displaying things in the background while still allowing user interaction on the CLI. The gist of it is this:
@staticmethod
def _input_timeout(prompt, timeout_secs):
class TimeExpiredException(Exception): pass
def raise_exception(signum, frame):
raise TimeExpiredException()
signal.signal(signal.SIGALRM, raise_exception)
try:
signal.alarm(timeout_secs)
result = input(prompt)
signal.alarm(0)
except TimeExpiredException:
result = None
return result
This program has mysteriously stopped working with Python 3.13.3 with an exceptionally strange error mode: my signal handler is called from somewhere I apparently do not expect (HelpFormatter), causing the whole process to terminate. This always happens after exactly 29 seconds when I run my process as user and after exactly 25 seconds when I run as root. It happens only when there is no __pycache__ present, i.e., when the Python bytecode is generated on startup.
I tried creating a minimal reproducer, which was super difficult. Even when I remove dead code from the Python script, the bug stops triggering. Some code removal was fine, but for example if I remove the wrapper around argparse, the bug disappears.
As I said. This sounds batshit crazy.
When the mystery signal is caught, it gives one final indication where it comes from:
reliant [/tmp/heisenbug]: rm -fr __pycache__/ && time ./stopwatch
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Exception ignored in tp_clear of HelpFormatter:
Traceback (most recent call last):
File "/tmp/heisenbug/./stopwatch", line 69, in raise_exception
raise TimeExpiredException()
StopWatch._input_timeout.<locals>.TimeExpiredException:
FINALLY
real 0m25,035s
user 0m0,025s
sys 0m0,011s
Note the message: Exception ignored in tp_clear of HelpFormatter: (this was running as root, you see that it took 25 seconds.)
Just so you believe me that it always triggers after the same time, here are three consecutive runs as root (I swear this is not copy paste, timings are accurate to millisecond level!):
reliant [/tmp/heisenbug]: rm -fr __pycache__/ && time ./stopwatch
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Exception ignored in tp_clear of HelpFormatter:
Traceback (most recent call last):
File "/tmp/heisenbug/./stopwatch", line 69, in raise_exception
raise TimeExpiredException()
StopWatch._input_timeout.<locals>.TimeExpiredException:
FINALLY
real 0m25,035s
user 0m0,025s
sys 0m0,011s
reliant [/tmp/heisenbug]: rm -fr __pycache__/ && time ./stopwatch
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Exception ignored in tp_clear of HelpFormatter:
Traceback (most recent call last):
File "/tmp/heisenbug/./stopwatch", line 69, in raise_exception
raise TimeExpiredException()
StopWatch._input_timeout.<locals>.TimeExpiredException:
FINALLY
real 0m25,035s
user 0m0,025s
sys 0m0,011s
reliant [/tmp/heisenbug]: rm -fr __pycache__/ && time ./stopwatch
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Exception ignored in tp_clear of HelpFormatter:
Traceback (most recent call last):
File "/tmp/heisenbug/./stopwatch", line 69, in raise_exception
raise TimeExpiredException()
StopWatch._input_timeout.<locals>.TimeExpiredException:
FINALLY
real 0m25,035s
user 0m0,025s
sys 0m0,010s
And here some runs as user:
reliant joe [/tmp/heisenbug]: rm -fr __pycache__ && time ./stopwatch
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Exception ignored in tp_clear of HelpFormatter:
Traceback (most recent call last):
File "/tmp/heisenbug/./stopwatch", line 69, in raise_exception
raise TimeExpiredException()
StopWatch._input_timeout.<locals>.TimeExpiredException:
FINALLY
real 0m29,038s
user 0m0,029s
sys 0m0,010s
reliant joe [/tmp/heisenbug]: rm -fr __pycache__ && time ./stopwatch
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Exception ignored in tp_clear of HelpFormatter:
Traceback (most recent call last):
File "/tmp/heisenbug/./stopwatch", line 69, in raise_exception
raise TimeExpiredException()
StopWatch._input_timeout.<locals>.TimeExpiredException:
FINALLY
real 0m29,039s
user 0m0,027s
sys 0m0,012s
reliant joe [/tmp/heisenbug]: rm -fr __pycache__ && time ./stopwatch
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Exception ignored in tp_clear of HelpFormatter:
Traceback (most recent call last):
File "/tmp/heisenbug/./stopwatch", line 69, in raise_exception
raise TimeExpiredException()
StopWatch._input_timeout.<locals>.TimeExpiredException:
FINALLY
real 0m29,038s
user 0m0,029s
sys 0m0,009s
I'm running on 6.14.0-15-generic #15-Ubuntu. My CPU is an AMD Ryzen Threadripper PRO 3945WX 12-Cores. I'm using the Ubuntu-provided Python package (Ubuntu calls it 3.13.3-1):
Python 3.13.3 (main, Apr 8 2025, 19:55:40) [GCC 14.2.0] on linux
I cannot reproduce this issue on Debian with Python 3.11/ARM nor on another Ubuntu with Python 3.12/x86_64.
I cannot attach files (the reproducer) to this bug, so here it is as base64:
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
3dZt3dZt3dZt3dZt3dZt3faN2v8Au7ZfrgBQAAA=
You can simply unpack it by doing:
$ openssl base64 -d | tar xvz
And pasting the above base64 blob in. Remember you have to clear __pycache__ to trigger the bug. I'm including the bytecode in case it contains something interesting.
Please someone tell me this is reproducible and I'm not completely losing my marbles.
CPython versions tested on:
3.13
Operating systems tested on:
Linux
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsTodo
Activity
[-]Heisenbug that kills process via SIGALARM[/-][+]Heisenbug that kills process via SIGALRM[/+]colesbury commentedon May 8, 2025
I haven't looked at the reproducer yet, but this is understandable if unexpected behavior. Exceptions raised during destructors (i.e.,
__del__methods) get printed as unraisable exceptions and then swallowed:https://docs.python.org/3/library/sys.html#sys.unraisablehook
That's not new behavior and it's pretty much a fundamental limitation of raising exception asynchronously, such as with
signal.signal(). Most likely, the timing of things slightly changed in 3.13 such that you routinely get "unlucky" as to what's executing when the SIGALRM triggers.johndoe31415 commentedon May 8, 2025
Interesting, so are you saying that raising an exception is always undefined behavior in Python, always has been?
The thing that makes me quite curious is that I do believe that in the scenario, this does look like it is well-defined behavior. I'm setting
signal.alarm()inside thetryblock, then cancel it before leaving the try block. AnySIGALRMshould call the installed signal handler and the raised exception should be possible to stack-unwind (there is only a single main thread there). In this instance, it seems to be the case that the signal is delivered while outside thetry...exceptblock, which should never be possible.Can you try to elaborate why this would be happening?
I would be fine with the exception being omitted (e.g., during
__del__, as you described) and being silently discarded. But somehow my program is not always able to catch the raised exception, which is indeed a problem.Thanks for taking the time to read through my report! Cheers.
ronaldoussoren commentedon May 10, 2025
There are signals involved here, and those result in asynchronous code execution in C. Integration of signal handlers in the rest of the interpreter is non-trivial.
Anyway:
johndoe31415 commentedon May 19, 2025
First of all, thanks @ronaldoussoren for trying it out and confirming that there is indeed an issue. I agree that signal handling is exceptionally tricky and I'll be the first to admit that stack unwinding in a signal handler is just nasty. That said, if it's not UB, then it's an interpreter bug.
I did some more digging and while
alarm(2)allows only for second granularity,setitimer(2)does not -- and also has a helpful Python wrapper. With that I could boil down a minimal reproducer that triggers fairly quickly:Also note that while I said I could not reproduce on Python 3.12, I absolutely can now! Case in point:
and
and
Note that I cannot produce the issue on an ARM64 virutal machine (did not trigger up until a counter value of 40000, when I aborted).
johndoe31415 commentedon May 25, 2025
So I did a lot of building and testing. Using this script:
I'm able to discern that a version is affected (return code != 0) or probably not affected (return code == 0). Note that since this is a nondeterministic issue, I can only ever verify presence of the issue, but not falsify it. This makes my analysis a bit messy.
First, I checked the 3.12, 3.13, 3.14 branches and all versions that have tags for the issue:
I built each version with
./configure --enable-optimizations --disable-test-modules. So a GIL should always be present.Then I bisected in each branch that has the issue occuring. For 3.13, the faulty commit is plausible:
For 3.12, the faulty commit is much less plausible and has me scratching my head:
Hope this helps a bit. Here are links to the faulty commits:
Also note that because the faulty commit in 3.12 seems unlikely (most of the commit is testing code, the only change I can see is the type conversion to IntEnum), I did this bisection multiple times and always got the same results. I'd happily do more testing if this is useful.
duaneg commentedon Jun 15, 2025
Not undefined in the C sense, but in practice very difficult to use safely. The exception can be raised from ~anywhere, and code that is not specifically and very carefully written with that expectation will break. Standard library & garbage collector code is not written like this (and realistically cannot be), so reliably using a signal handler that raises involves, amongst other things, not calling into the standard library or allowing the garbage collector to run.
I am not the person you asked, but I'll try to explain (and hopefully if I get anything wrong someone more knowledgeable will correct me)...
In your example the signal will be delivered inside that
try...exceptblock, but theinputcall descends into interpreter internals and the standard library and will do a bunch of things, including possibly running the garbage collector, which is the source of the "ignored" error you are seeing. The garbage collector is callingtp_clearon some deleted garbage, then checking for an error. In this case, it finds one! The error was caused by the signal, not directly by the code it called, but it has no way to know that, and couldn't do anything useful with the information even if it did.GC has no way to gracefully handle such errors, so it prints that "ignored" message as a last ditch attempt to provide the user some indication as to why everything is going/about to go wrong. It cannot propagate the exception out and unwind the stack, since the garbage object which it thinks is throwing from
tp_clearcould have been allocated by some other totally unrelated code.