Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[PATCH 2/5] patmatch(): give traps run mid-match their own pattern buffer



patmatch() runs queued signal handlers while matching
(check_for_signals()).  If the pattern being matched was compiled with
PAT_STATIC (as compgetmatch() does for ${x#pat}, ${x:#pat} etc.), the
program lives in the shared patout buffer.  A trap that compiles
another PAT_STATIC pattern, e.g. via a "case" statement, reallocates
patout and the interrupted match continues on freed memory, typically
crashing at "next = PATNEXT(scan)".

Save patout/patalloc around the handlers and let them allocate a
buffer of their own, freed afterwards.

This is the crash seen when holding Ctrl-C at a powerlevel10k prompt:
SIGSEGV in patmatch() from pattryrefs(), during prompt expansion.

Co-Authored-By: Claude Opus 5.5 <noreply@xxxxxxxxxxxxx>
---

Notes:
    Reproducer: start "zsh -f", paste the lines below, then hold down
    Ctrl-C for a few seconds.  Without this patch (preceding ones applied),
    zsh 5.9.2 segfaults in patmatch(); AddressSanitizer reports heap-use-after-free.
    
        setopt interactivecomments
        # 2: a trap run from inside patmatch() compiles a PAT_STATIC pattern
        #    (case), reallocating the buffer the interrupted match still uses.
        #    (Signals stay queued while a function runs; patmatch() runs them.
        #    The zle -F handler on /dev/zero keeps the match running.)
        p="(${(j:|:)${(s: :)${:-$(print -- {1..100}c)}}})"
        a=( {1..2000}x )
        exec {z}</dev/zero
        h() { local c x; read -r -u $1 -k 1 c; for x in ${a:#*${~p}}; do done }
        zle -F $z h
        trap 'case $RANDOM in (${~p}q|${~p}r) ;; esac; return 130' INT

 Src/pattern.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Src/pattern.c b/Src/pattern.c
index 017c20a1e..7d8bef960 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -2714,8 +2714,17 @@ patmatch(Upat prog)
 	    int savpatflags = patflags, savpatglobflags = patglobflags; \
             char *savexactpos = exactpos, *savexactend = exactend; \
 	    struct rpat savpattrystate = pattrystate; \
+	    char *savpatout = patout; \
+	    long savpatalloc = patalloc; \
+	    /* prog may live in patout (PAT_STATIC): traps get their own */ \
+	    patout = NULL; \
+	    patalloc = 0; \
 	    dont_queue_signals(); \
 	    restore_queue_signals(q); \
+	    if (patout) \
+		zfree(patout, patalloc); \
+	    patout = savpatout; \
+	    patalloc = savpatalloc; \
 	    exactpos = savexactpos; \
 	    exactend = savexactend; \
 	    patflags = savpatflags; \
-- 
2.55.0





Messages sorted by: Reverse Date, Date, Thread, Author