From 7a2d3564b68be537d3f68188e3f0c5df2b45dd13 Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Mon, 3 May 2021 18:27:44 +0100 Subject: [PATCH] emacs/vi: Fix behaviour after command substitution Tab completion in emacs and vi wrongly parses and executes command substitutions. Example reproducers: $ $(~) # Result: $ $(~)ksh[1]: /home/johno: cannot execute [Is a directory] $ $(~ksh) # Result: $ $(~ksh)ksh: /home/johno/GitRepos/KornShell/ksh: cannot execute [Is a directory] $ $(echo true) # Result: $ /usr/bin/true # or just 'true' -- it's unpredictable In addition, backtick command substitutions had the following bug: $ `echo hi` # Result: $ `echo hi`ksh: line 1: BUG_BRACQUOT_test.sh: not found (where BUG_BRACQUOT_test.sh happens to be lexically the first-listed file in my ksh development working directory). There's also a crash associated with this due to an access beyond buffer boundaries, which is only triggered on some systems (macOS included). src/cmd/ksh93/edit/completion.c: - find_begin(): * When finding the beginning of a command substitution and the last character is ')', do not increase the character pointer cp. Increasing it caused the condition 'if(c && c==endchar)' in the 'default:' block to be true, causing 'return(xp);' to be executed, which returns a pointer the beginning of the command substitution to ed_expand() on line 290, so that ed_expand() eventually executes the command substitution with the sh_argbuild() call on line 349. After deleting this 'else cp++', that statement 'if(c && c==endchar) return(xp);' is not executed and `find_begin()` returns the null pointer, which avoids anything being executed. Thanks to @JohnoKing: https://github.com/ksh93/ksh/issues/268#issuecomment-817249164 * Add code for properly skipping over backtick-style command substitutions, based on the $( ) code. - ed_expand(): Avoid out[-1] reading one byte to the left of outbuff by first checking that out>outbuff. Thanks to @JohnoKing for using ASan to find the location of the crash: https://github.com/ksh93/ksh/issues/268#issuecomment-825574885 src/cmd/ksh93/tests/pty.sh: - Test for the bugs detailed above. Resolves: https://github.com/ksh93/ksh/issues/268 --- src/cmd/ksh93/edit/completion.c | 14 ++++++++++---- src/cmd/ksh93/tests/pty.sh | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/cmd/ksh93/edit/completion.c b/src/cmd/ksh93/edit/completion.c index fe9261583..7125373da 100644 --- a/src/cmd/ksh93/edit/completion.c +++ b/src/cmd/ksh93/edit/completion.c @@ -173,10 +173,16 @@ static char *find_begin(char outbuff[], char *last, int endchar, int *type) xp = find_begin(cp,last,')',type); if(*(cp=xp)!=')') bp = xp; - else - cp++; } break; + case '`': + if(inquote=='\'') + break; + *type = mode; + xp = find_begin(cp,last,'`',type); + if(*(cp=xp)!='`') + bp = xp; + break; case '=': if(!inquote) { @@ -447,9 +453,9 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) out = overlaid(begin,*com++,nocase); } mode = (out==saveout); - if(out[-1]==0) + if(out>outbuff && out[-1]==0) out--; - if(mode && out[-1]!='/') + if(mode && (out==outbuff || out>outbuff && out[-1]!='/')) { if(cmd_completion) { diff --git a/src/cmd/ksh93/tests/pty.sh b/src/cmd/ksh93/tests/pty.sh index 7d5f0c317..ddf45ddee 100755 --- a/src/cmd/ksh93/tests/pty.sh +++ b/src/cmd/ksh93/tests/pty.sh @@ -858,5 +858,19 @@ w test \$\{.sh.level\t r ^:test-1: test \$\{.sh.level\}\r\n$ ! +# err_exit # +((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!" +L tab completion executes command substitutions +# https://github.com/ksh93/ksh/issues/268 + +d 15 +p :test-1: +w $(echo true)\t +r ^:test-1: \$\(echo true\)\r\n$ +p :test-2: +w `echo true`\t +r ^:test-2: `echo true`\r\n$ +! + # ====== exit $((Errors<125?Errors:125))