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

[PATCH 5/5] raw_getbyte(): keep fds[] and watch_fds in step when traps run



A trap run while ZLE waits for input may add "zle -F" handlers
(powerlevel10k's SIGINT trap does).  raw_getbyte() then copied nwatch
entries and indexed fds[] with them although fds[] was sized for the
old count; likewise the zrealloc() of fds[] can itself run queued
signals between computing nfds and filling the array
(AddressSanitizer: heap-buffer-overflow in raw_getbyte()).

Only handle as many watchers as were polled, and queue signals while
taking the copy and while resizing fds[].

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),
    AddressSanitizer reports heap-buffer-overflow in raw_getbyte().
    
        setopt interactivecomments
        # 5: a trap adding "zle -F" watchers while zle handles watchers: the
        #    handler loop indexes fds[] with the new, larger watcher count.
        exec {z}</dev/zero
        h() { local c; read -r -u $1 -k 1 c }
        zle -F $z h
        w() { zle -F $1; exec {1}<&- }
        t() { local f; exec {f}</dev/null; zle -F $f w }
        trap 't; return 130' INT

 Src/Zle/zle_main.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 921ebb9ec..c044de46f 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -719,11 +719,24 @@ raw_getbyte(long do_keytmout, char *cptr, int full)
 		 * user decides to delete one from inside the
 		 * handler function.
 		 */
-		int lnwatch = nwatch;
-		Watch_fd lwatch_fds = zalloc(lnwatch*sizeof(struct watch_fd));
+		int lnwatch;
+		Watch_fd lwatch_fds;
+
+		/*
+		 * A trap run while we were waiting may have changed
+		 * watch_fds; only the ones we polled have results.
+		 */
+		queue_signals();
+		lnwatch = nwatch;
+# ifdef HAVE_POLL
+		if (lnwatch > nfds - 1)
+		    lnwatch = nfds - 1;
+# endif
+		lwatch_fds = zalloc(lnwatch*sizeof(struct watch_fd));
 		memcpy(lwatch_fds, watch_fds, lnwatch*sizeof(struct watch_fd));
 		for (i = 0; i < lnwatch; i++)
 		    lwatch_fds[i].func = ztrdup(lwatch_fds[i].func);
+		unqueue_signals();
 		for (i = 0; i < lnwatch; i++) {
 		    Watch_fd lwatch_fd = lwatch_fds + i;
 		    if (
@@ -792,6 +805,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full)
 
 # ifdef HAVE_POLL
 		/* Function may have added or removed handlers */
+		queue_signals();	/* keep nwatch and fds[] in step */
 		nfds = 1 + nwatch;
 		if (nfds > 1) {
 		    fds = zrealloc(fds, sizeof(struct pollfd) * nfds);
@@ -813,6 +827,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full)
 			fds[i+1].revents = 0;
 		    }
 		}
+		unqueue_signals();
 # endif
 	    }
 	    /* If looping, need to recalculate timeout */
-- 
2.55.0





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