From 55023e5a1e97d65966fc3174a6796e678b25ef88 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Fri, 7 Aug 2026 12:35:22 +0200 Subject: [PATCH] fix zsh segfault bug from 5.9.1-1 onwards > Minimal reproduction: run the following > ``` > (echo 1 | cat & (echo 2)) > ``` > > There's no segfault when: > > Reverting to 5.9-6 > removing either of the parenthesis > removing any command in the chain (replacing them can still segfault; my original code is quite different) After break fork and fiddling with set follow-fork-mode I managed to follow the correct child, Thread 4.1 "zsh" received signal SIGSEGV, Segmentation fault. 0x0000000000462cde in havefiles () at jobs.c:1620 1620 peekfirst(jobtab[i].filelist)) { (gdb) bt eparams=0x7ffcf79979c0, input=0, output=0, how=18, last1=1, close_if_forked=-1) at exec.c:3719 how=18, input=0, output=0, last1=1) at exec.c:2070 slcode=10242, how=18, last1=1) at exec.c:1795 (gdb) p *jobtab[i].filelist $2 = {list = {first = 0x0, last = 0x22af360, flags = 0}, node = {next = 0x0, prev = 0x22af360, dat = 0x0}} As we can see here, first is NULL, and peekfirst does ->list.first->dat which crashes. The problem was introduced in zw/51404: Nullify filelist after deleting (fix segfault) d3edf318306e37d2d96. The problem isn't that it nullifies the filelist, but how it checks for that later. I think this is the fix: --- Src/jobs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/jobs.c b/Src/jobs.c index 106fbc7cf..4d3fb1fee 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1552,7 +1552,7 @@ havefiles(void) for (i = 1; i <= maxjob; i++) if (jobtab[i].stat && jobtab[i].filelist && - peekfirst(jobtab[i].filelist)) { + nonempty(jobtab[i].filelist)) { return 1; } return 0; -- 2.56.0