On 10/29/18 12:01 PM, Anton Lindqvist wrote:
> On Mon, Oct 29, 2018 at 09:55:38AM +0100, Martijn van Duren wrote:
>> On 10/28/18 8:13 PM, Anton Lindqvist wrote:
>>> Hi,
>>> Bug reported by miod@, how to reproduce:
>>>
>>>   $ command -v r
>>>   alias r='fc -s'
>>>   $ sleep 5
>>>   $ r sleep
>>>   ^C # abort sleep before finishing
>>>   $ r sleep
>>>   ksh: fc: history function called recursively
>>>
>>> The c_fc() function has some internal state used to prevent recursive
>>> invocations that gets out of sync when the re-executed command is
>>> aborted by SIGINT. My proposal is to temporarily register a SIGINT
>>> handler before re-executing the command in order to reset the call depth
>>> counter upon signal delivery.
>>>
>>> Comments? OK?
>>>
>> You diff always unsets the shtrap pointer, which most likely unsets
>> custom SIGINT handlers installed by the user (untested). Next to that
>> this feels like special-casing and I'm afraid that there are some cases
>> we might be overlooking here.
> 
> Thanks for the feedback. A few drive by thoughts, will look more closely
> this evening: a user supplied SIGINT handler is never registered as a
> one shot handler so it should never be overwritten. The hist_sigint()
> one shot handler is registered before executing the command, if the
> command does register a SIGINT handler it takes higher precedence and
> hist_execute() will return under such circumstances implying that
> fc_depth will be correctly decremented. The one shot handler is only
> used when the command does not register a SIGINT handler.
> 
>>
>> The source with this issue is that a SIGINT triggers a list of
>> siglongjmps, which don't allow us to return to c_fc and decrement the
>> pointer. The diff below detects if we're on the lowest level of the
>> unwinding by abusing the interactive variable. This works because
>> calling fc from a non-interactive session is prohibited.
>>
>> I'm not 100% convinced that the placement of fc_depth = 0 should be
>> placed in shell(), or that we maybe should place it in unwind(). The
>> reason I choose for shell() is because interactive is guaranteed to
>> be setup for checking, because it's already there.
>>
>> This diff is only lightly tested and the code here is very hard to
>> untangle, so extreme scrutiny is desirable.
>>
>> martijn@
>>
After some back and forth with anton@ we came up with the following
diff. I would like to have some extra scrutiny on this one before
actually committing this.

martijn@

Index: history.c
===================================================================
RCS file: /cvs/src/bin/ksh/history.c,v
retrieving revision 1.80
diff -u -p -r1.80 history.c
--- history.c   15 Jan 2018 22:30:38 -0000      1.80
+++ history.c   12 Nov 2018 15:46:35 -0000
@@ -48,6 +48,8 @@ static uint32_t       line_co;
 
 static struct stat last_sb;
 
+static volatile sig_atomic_t   c_fc_depth;
+
 int
 c_fc(char **wp)
 {
@@ -58,9 +60,8 @@ c_fc(char **wp)
        int optc, ret;
        char *first = NULL, *last = NULL;
        char **hfirst, **hlast, **hp;
-       static int depth;
 
-       if (depth != 0) {
+       if (c_fc_depth != 0) {
                bi_errorf("history function called recursively");
                return 1;
        }
@@ -146,9 +147,9 @@ c_fc(char **wp)
                    hist_get_newest(false);
                if (!hp)
                        return 1;
-               depth++;
+               c_fc_depth++;
                ret = hist_replace(hp, pat, rep, gflag);
-               depth--;
+               c_fc_reset();
                return ret;
        }
 
@@ -268,11 +269,20 @@ c_fc(char **wp)
                shf_close(shf);
                *xp = '\0';
                strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
-               depth++;
+               c_fc_depth++;
                ret = hist_execute(Xstring(xs, xp));
-               depth--;
+               c_fc_reset();
                return ret;
        }
+}
+
+/* Reset the c_fc depth counter.
+ * Made available for when an fc call is interrupted.
+ */
+void
+c_fc_reset(void)
+{
+       c_fc_depth = 0;
 }
 
 /* Save cmd in history, execute cmd (cmd gets trashed) */
Index: main.c
===================================================================
RCS file: /cvs/src/bin/ksh/main.c,v
retrieving revision 1.93
diff -u -p -r1.93 main.c
--- main.c      29 Sep 2018 14:13:19 -0000      1.93
+++ main.c      12 Nov 2018 15:46:35 -0000
@@ -549,6 +549,7 @@ shell(Source *volatile s, volatile int t
                case LERROR:
                case LSHELL:
                        if (interactive) {
+                               c_fc_reset();
                                if (i == LINTR)
                                        shellf("\n");
                                /* Reset any eof that was read as part of a
Index: sh.h
===================================================================
RCS file: /cvs/src/bin/ksh/sh.h,v
retrieving revision 1.73
diff -u -p -r1.73 sh.h
--- sh.h        18 May 2018 13:25:20 -0000      1.73
+++ sh.h        12 Nov 2018 15:46:35 -0000
@@ -449,6 +449,7 @@ void        hist_init(Source *);
 void   hist_finish(void);
 void   histsave(int, const char *, int);
 int    c_fc(char **);
+void   c_fc_reset(void);
 void   sethistcontrol(const char *);
 void   sethistsize(int);
 void   sethistfile(const char *);

Reply via email to