On Mon, Nov 12 2018, Martijn van Duren <openbsd+t...@list.imperialat.at> wrote: > 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.
Even if this looks more like an ad-hoc solution, I find it easier to grasp and review than the trap-based diff. Works fine here and seems to solve miod's problem. ok jca@ > 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 *); > -- jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF DDCC 0DFA 74AE 1524 E7EE