-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Segfault on processing a here-document from a command substitution in a here-document #823
Comments
This does appear to be a very system-specific crash. I can only reproduce it on Alpine Linux (musl libc, arm64). I cannot reproduce it on Void Linux (also musl libc on arm64), Linux with glibc, or macOS. On Alpine, the gdb backtrace (for the current 1.0 branch) is:
|
I can confirm that this is only any issue on my musl-libc system when |
Unfortunately,
So, the bug must be in here-document processing somewhere. |
I tracked down where the buffer pointer becomes corrupted in lex_advance(). It is here: Line 152 in ea42e0f
The execution of command substitutions involves parsing and lexical analysis at runtime. A command substitution combined with nested execution of here-documents causes lp->lexd.first to be changed, without being restored when the nested execution returns. The following patch saves and restores it when running a command substitution from a here-document, which appears to fix the bug on the system on which I can reproduce it. diff --git a/src/cmd/ksh93/sh/macro.c b/src/cmd/ksh93/sh/macro.c
index 52545a40..043094fd 100644
--- a/src/cmd/ksh93/sh/macro.c
+++ b/src/cmd/ksh93/sh/macro.c
@@ -381,8 +381,12 @@ void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string)
break;
}
case S_PAR:
+ {
+ char *savefirst = lp->lexd.first;
comsubst(mp,NULL,1);
+ lp->lexd.first = savefirst;
break;
+ }
case S_EOF:
if((c=fcfill()) > 0)
goto again; |
So, the issue occurs when a here-document is processed from a command substitution within a here-document. The TEST_LINE="Test line"
cat <<EOF
$(
cat <<EOF2
${TEST_LINE}
EOF2
)
EOF |
Both here-documents and command substitutions (and arithmetic expressions) involve lexical analysis at runtime. So it is probably safest to save and restore the entire lexer state struct, not just that one diff --git a/src/cmd/ksh93/sh/macro.c b/src/cmd/ksh93/sh/macro.c
index 52545a40..8fb91b93 100644
--- a/src/cmd/ksh93/sh/macro.c
+++ b/src/cmd/ksh93/sh/macro.c
@@ -381,8 +381,17 @@ void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string)
break;
}
case S_PAR:
+ {
+ /*
+ * Execute a command substitution or arithmetic expression from a here-document. As both
+ * these and here-documents involve lexical analysis at runtime, save and restore the
+ * lexer state to avoid hard-to-trace invalid pointers down the line in case of nesting.
+ */
+ Lex_t sav = *lp;
comsubst(mp,NULL,1);
+ *lp = sav;
break;
+ }
case S_EOF:
if((c=fcfill()) > 0)
goto again; |
Minimal reproducer: TEST_LINE="Test line" cat <<EOF $( cat <<EOF2 ${TEST_LINE} EOF2 ) EOF This script crashes on (at least) Alpine Linux with musl libc. I have not been able to get it to crash on any other system, even with ASan or valgrind, but still, this indicates a problem. The relevant part of the gdb(1) stack trace on that system is: #0 memcpy () at src/string/aarch64/memcpy.S:145 #1 0x0000aaaaaabd4d7c in sfwrite (f=0xaaaaaac813d8 <_Stak_data>, buf=0xfffff7eb127b, n=225211) at /usr/local/src/ksh/src/lib/libast/sfio/sfwrite.c:130 #2 0x0000aaaaaaae7f28 in lex_advance (iop=0xfffff7fe0600, buff=0xfffff7eb127b <error: Cannot access memory at address 0xfffff7eb127b>, size=225211, context=0xaaaaaac8aae0) at /usr/local/src/ksh/src/cmd/ksh93/sh/lex.c:161 #3 0x0000aaaaaaad10e8 in fcfill () at /usr/local/src/ksh/src/cmd/ksh93/sh/fcin.c:98 #4 0x0000aaaaaaaf17a0 in sh_machere (infile=0xfffff7fe0600, outfile=0xfffff7fe3140, string=0xfffff7fe3c51 "EOF") at /usr/local/src/ksh/src/cmd/ksh93/sh/macro.c:319 #5 0x0000aaaaaaadf64c in io_heredoc (iop=0xfffff7fe3c00, name=0xfffff7fe3c51 "EOF", traceon=1) at /usr/local/src/ksh/src/cmd/ksh93/sh/io.c:1632 #6 0x0000aaaaaaade36c in sh_redirect (iop=0xfffff7fe3c00, flag=0) at /usr/local/src/ksh/src/cmd/ksh93/sh/io.c:1245 #7 0x0000aaaaaab27898 in sh_exec (t=0xfffff7fe3bc0, flags=4) at /usr/local/src/ksh/src/cmd/ksh93/sh/xec.c:1176 So, as we can see in #2, the problem is an invalid pointer. It becomes invalid in lex_advance() in lex.c on line 152, which is: buff = lp->lexd.first; So it looks like the lp->lexd.first pointer is somehow invalidated. Analysis: The execution of both here-documents and command substitutions involves parsing and lexical analysis at runtime. A command substitution combined with nested execution of here- documents causes lp->lexd.first to be changed, without being restored when the nested execution returns. So, this suggests that the fix would be to save that pointer before calling comsubst() from sh_machere() to execute a command substitution from within a here-document, and restore it after. But, to avoid other potential or unforeseen problems, it is probably best to save and restore the entire lexer state struct, not just that one 'first' pointer. src/cmd/ksh93/sh/macro.c: sh_machere(): - When executing a command substitution or arithmetic expression from a here-document, save and restore the lexer state. Thanks to @stefanbidi for the bug report. Resolves: #823
Nope. That last fix causes a different crash. So, force-pushed back out, and bug reopened. |
So, not restoring New strategy: just reset it. The existing lex.c code handles this. diff --git a/src/cmd/ksh93/sh/macro.c b/src/cmd/ksh93/sh/macro.c
index 52545a40..9dc96538 100644
--- a/src/cmd/ksh93/sh/macro.c
+++ b/src/cmd/ksh93/sh/macro.c
@@ -382,6 +382,7 @@ void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string)
}
case S_PAR:
comsubst(mp,NULL,1);
+ lp->lexd.first = NULL;
break;
case S_EOF:
if((c=fcfill()) > 0) |
@stefanbidi, I would appreciate it if you could test the third patch, above. |
I can confirm that the latest patch fixes the issue for me. The test case no longer segfaults and Binutils 2.44 now builds with correct linker scripts. Thank you very much! |
Minimal reproducer: TEST_LINE="Test line" cat <<EOF $( cat <<EOF2 ${TEST_LINE} EOF2 ) EOF This script crashes on (at least) Alpine Linux with musl libc. I have not been able to get it to crash on any other system, even with ASan or valgrind, but still, this indicates a problem. The relevant part of the gdb(1) stack trace on that system is: #0 memcpy () at src/string/aarch64/memcpy.S:145 #1 0x0000aaaaaabd4d7c in sfwrite (f=0xaaaaaac813d8 <_Stak_data>, buf=0xfffff7eb127b, n=225211) at /usr/local/src/ksh/src/lib/libast/sfio/sfwrite.c:130 #2 0x0000aaaaaaae7f28 in lex_advance (iop=0xfffff7fe0600, buff=0xfffff7eb127b <error: Cannot access memory at address 0xfffff7eb127b>, size=225211, context=0xaaaaaac8aae0) at /usr/local/src/ksh/src/cmd/ksh93/sh/lex.c:161 #3 0x0000aaaaaaad10e8 in fcfill () at /usr/local/src/ksh/src/cmd/ksh93/sh/fcin.c:98 #4 0x0000aaaaaaaf17a0 in sh_machere (infile=0xfffff7fe0600, outfile=0xfffff7fe3140, string=0xfffff7fe3c51 "EOF") at /usr/local/src/ksh/src/cmd/ksh93/sh/macro.c:319 #5 0x0000aaaaaaadf64c in io_heredoc (iop=0xfffff7fe3c00, name=0xfffff7fe3c51 "EOF", traceon=1) at /usr/local/src/ksh/src/cmd/ksh93/sh/io.c:1632 #6 0x0000aaaaaaade36c in sh_redirect (iop=0xfffff7fe3c00, flag=0) at /usr/local/src/ksh/src/cmd/ksh93/sh/io.c:1245 #7 0x0000aaaaaab27898 in sh_exec (t=0xfffff7fe3bc0, flags=4) at /usr/local/src/ksh/src/cmd/ksh93/sh/xec.c:1176 So, as we can see in #2, the problem is an invalid pointer. It becomes invalid in lex_advance() in lex.c on line 152, which is: buff = lp->lexd.first; So it looks like the lp->lexd.first pointer is somehow invalidated. Analysis: The execution of both here-documents and command substitutions involves parsing and lexical analysis at runtime. A command substitution combined with nested execution of here- documents causes lp->lexd.first to be changed, without being restored when the nested execution returns. So, this suggests that the fix would be to save that pointer before calling comsubst() from sh_machere() to execute a command substitution from within a here-document, and restore it after. However, restoring it causes another, very similar crash when running the regression tests from the modernish shell library. So we can't rely on that pointer being valid either way. src/cmd/ksh93/sh/macro.c: sh_machere(): - When executing a command substitution or arithmetic expression from a here-document, reset lp->lexd.first to NULL to avoid a potentially invalid address being used in lex_advance(). The lex.c code already handles the case of it being NULL. Thanks to @stefanbidi for the bug report. Resolves: #823
A little background before I go into it... I ran into this issue when trying to troubleshoot an issue with Binutils 2.44 on a x86_64-pc-linux-musl system running ksh93u+m version 1.0.10. A link to the bug report I found in the Binutils mailing list can be found here:
https://sourceware.org/bugzilla/show_bug.cgi?id=32580
Clearly the issue predates Binutils 2.44 by many years as the person who posted that bug report is using Solaris 2.11. Essentially, ksh93 crashes when running a very simple script with a few functions:
This script produces the right output using bash, dash and oksh, however, when run with ksh93 it seg faults:
Please let me know if I can provide any more information.
The text was updated successfully, but these errors were encountered: