Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH 0/5] crashes when traps run re-entrantly (holding Ctrl-C at a powerlevel10k prompt)
- X-seq: zsh-workers 55170
- From: Imre Péntek <imre@xxxxxxxxxx>
- To: zsh-workers@xxxxxxx
- Cc: Imre Péntek <imre@xxxxxxxxxx>
- Subject: [PATCH 0/5] crashes when traps run re-entrantly (holding Ctrl-C at a powerlevel10k prompt)
- Date: Wed, 23 Sep 2026 19:08:20 +0200
- Archived-at: <https://zsh.org/workers/55170>
- List-id: <zsh-workers.zsh.org>
Holding down Ctrl-C at a powerlevel10k prompt makes zsh 5.9.2 segfault
within a second or two (Arch/Manjaro zsh 5.9.2-1, crash in patmatch()
called from pattryrefs() during prompt expansion).
powerlevel10k's SIGINT trap does a lot of work (it redraws the prompt,
every function starts with "emulate -L zsh" and re-installs the INT
trap from an EXIT trap, it adds "zle -F" handlers), so a stream of
SIGINTs runs that trap in the middle of other code, and nested in
itself. Running zsh under AddressSanitizer with that setup found five
places where state shared with the interrupted code is not preserved
across the trap; each patch fixes one:
1/5 dotrap(): the trap's Eprog can be freed by a nested run of the
same trap before it is executed (heap-use-after-free)
2/5 patmatch(): a trap run mid-match reallocates the PAT_STATIC
buffer the match is using (the segfault above)
3/5 makezleparams(): NULL dereference of $registers in nested traps
while ZLE is active (also seen as a segfault)
4/5 installemulation(): nested "emulate" leaves the outer option
scan writing to a dead stack frame
5/5 raw_getbyte(): fds[] overflow when a trap adds "zle -F" handlers
Each patch has a reproducer below its "---" line: start "zsh -f",
paste the lines, hold down Ctrl-C. Each reproducer fails with the
preceding patches applied and passes with its own patch. Reproducers
2 and 3 also crash a stock (non-ASan) 5.9.2; the others are only
detected by AddressSanitizer.
The patches are against master (26ba0e39b) and also apply to 5.9.2.
Testing, on 5.9.2 built with clang -fsanitize=address, one build per
cumulative patch:
- with all five, each reproducer survives ~1000-2000 Ctrl-C in 5-10s,
as does the full powerlevel10k setup (which crashes stock zsh within
~0.1-5s every time);
- "make check" with all five: 63 successful, 0 failures, 2 skipped.
(Y03arguments failed intermittently in earlier runs, equally with
and without the patches.)
I used an AI assistant (Claude) to analyze the crash, write these
patches and the reproducers; I have reviewed and tested them.
Imre Péntek (5):
dotrap(): keep the trap's Eprog alive across dont_queue_signals()
patmatch(): give traps run mid-match their own pattern buffer
makezleparams(): don't dereference NULL $registers
installemulation(): restore static state after the option scan
raw_getbyte(): keep fds[] and watch_fds in step when traps run
Src/Zle/zle_main.c | 19 +++++++++++++++++--
Src/Zle/zle_params.c | 7 +++++--
Src/options.c | 6 ++++++
Src/pattern.c | 9 +++++++++
Src/signals.c | 15 +++++++++++++--
5 files changed, 50 insertions(+), 6 deletions(-)
--
2.55.0
Messages sorted by:
Reverse Date,
Date,
Thread,
Author