Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Otto Moerbeek
On Mon, Aug 09, 2021 at 07:20:31AM -0600, Theo de Raadt wrote:

> Ingo Schwarze  wrote:
> 
> > as mentioned earlier, deraadt@ reported that sftp(1) ignores Ctrl-C.
> > Fixing that without longjmp(3) requires making editline(3) better
> > behaved.
> 
> If a library interface encourages use longjmp(), that library should be
> thrown into the dustbin of history.  Our src tree has very few longjmp
> these days.  Thank you for the effort to discourage addition of more.
> 
> > The following patch causes el_gets(3) and el_wgets(3) to return
> > failure when read(2)ing from the terminal is interrupted by a
> > signal other than SIGCONT or SIGWINCH.  That allows the calling
> > program to decide what to do, usually either exit the program or
> > provide a fresh prompt to the user.
> 
> Looks good.
> 
> >  * bc(1)
> >It behaves well with the patch: Ctrl-C discards the current
> >input line and starts a new input line.
> >The reason why this already works even without the patch
> >is that bc(1) does very scary stuff inside the signal handler:
> >pass a file-global EditLine pointer on the heap to el_line(3)
> >and access fields inside the returned struct.  Needless to
> >say that no signal handler should do such things...
> 
> Otto -- if you are short of time, at minimum mark the variable usage
> line with /* XXX signal race */ as we have done throughout the tree.  I
> would encourage anyone who sees such problems inside any signal handler
> to show such comment-adding diffs.  If these problems are documented,
> they can be fixed eventually, usually through event-loop logic, but I'll
> admit many of the comments are in non-event-loop programs.

Added the comment. Don't know what I was thinking when I did that change.

-Otto

> 
> >  * ftp(1)
> >It behaves well with the patch: Ctrl-C discards the current
> >input line and provides a fresh prompt.
> >The reason why it already works without the patch is that ftp(1)
> >uses setjmp(3)/longjmp(3) to forcefully grab back control
> >from el_gets(3) without waiting for it to return.
> 
> Horrible isn't it.
> 
> >  * sftp(1)
> >Behaviour is improved with the patch: Ctrl-C now exits sftp(1).
> >If desired, i can supply a very simple follow-up patch to sftp.c
> >to instead behave like ftp(1) and bc(1), i.e. discard the
> >current input line and provide a fresh prompt.
> >Neither doing undue work in the signal handler nor longjmp(3)
> >will be required for that (if this patch gets committed).
> 
> I suspect dtucker will want to decide on the interactive behaviour,
> but what you describe sounds right.
> 
> > Also note that deraadt@ pointed out in private mail to me that the
> > fact that read__fixio() clears FIONBIO is probably a symptom of
> > botched editline(3) API design.  That might be worth fixing, too,
> > as far as that is feasible, but it is unrelated to the sftp(1)
> > Ctrl-C issue; let's address one topic at a time.
> 
> I mentioned rarely having seen a good outcome from code mixing any of
> the 3: FIONBIO, FIONREAD, and select/poll.  Often the non-blocking was
> added to select/poll code to hide some sort of bug, or the select/poll
> code was added amateurishly to older code without removing the FIONBIO.
> There are a few situations you need both approaches mixed, but it isn't
> the general case, and thus FIONBIO has a "polled IO" smell for me.
> 



Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Martijn van Duren
On Mon, 2021-08-09 at 15:17 +0200, Ingo Schwarze wrote:
> Hi Martijn,
> 
> Martijn van Duren wrote on Mon, Aug 09, 2021 at 02:15:42PM +0200:
> 
> > If we're stripping down fixio to a single function, why not
> > inline the whole thing?
> 
> I deliberately tried to:
> 
>  1. Keep patches that change behaviour as small as possible to make
>     review as simple as possible for people who fear they might be
>     affected but are not specifically interested in editline(3).
> 
>  2. Make sure that reorg / cleanup patches do not change behaviour.
> 
> > Also, as far as my brain explains things to me, it could theoretically
> > be possible that the signal handler kicks in right after we return a -1
> > from read(2), making it possible to get something like an EIO and
> > entering the sig switch statement. Assuming that a signal handler
> > doesn't clobber our errno (which libedit doesn't do, but who knows what
> > bad code is out there) we should probably only check the signal type
> > when we know we have an EINTR.
> 
> Yes, that looks like a race condition bug that so far escaped my
> attention.  But it seems unrelated to what should happen with signals
> in general, so can we keep fixing that bug separate?
> 
> > Finally, I don't understand why we only have a single retry on EAGAIN?
> 
> Not having *any* retries inside read_char() would look like a worthy
> long-term goal to me, but i'm not yet completely sure that can be
> reached.  I would prefer to steer into the direction of fewer magic
> retries rather than more of them.  Either way, EAGAIN seems unrelated
> to SIGINT, so i'd prefer to keep topics separate.
> 
> > If the application keeps resetting the FIONBIO in such a furious way
> > that it happens more then once in a single call (no idea how that could
> > happen) it might be an uphill battle, but we just burn away cpu cycles.
> > It is not and should probably not be treated like something fatal.
> 
> Actually, if the application sets FIONBIO at all (even once), chances
> are quite low in the first place that stuff works as inteded by the
> author of the application.  So i certainly wouldn't worry about an
> application setting FIONBIO in a loop, we have significantly worse
> problems than that.  But again, that's a separate topic.
> 
> > Diff below does this. (minus 27 LoC)
> 
> I do not in general object to cleaning this code up, and getting rid
> of read__fixio() indeed seems to be a long-term goal.  But i hope
> we will be able to remove all the (mostly broken) functionality
> from read__fixio() in a step-by-step manner, and once the function
> is empty, it will fade away without having to disturb the code in
> read_char().  Either way - separate topic...
> 
> The most important short-term goal seems to fix sftp(1), including
> the steps required to get that done in a clean way.
> 
> > The following ports use libedit (there might be a better way of
> > finding them, but this works):
> 
> Hmm, thanks, that list feels useful.
> 
> > So if we decide which of our interpretations should take precedence
> > it might be a good idea to put it into snaps for a while.
> 
> I don't think so in this case.  Let's not over-use the feature of
> putting stuff in snaps.  I think that should be reserved for stuff
> that is quite important and somewhat urgent and can't easily be
> tested in a less disruptive way.  But here, testing a program is
> quite feasible once you know which program to test.
> 
> Besides, *if* this patch causes a change in behaviour of a port,
> the most likely change is that a program that now ignores SIGINT
> exits on SIGINT afterwards.  That may be worth investigating and
> making a decision on in each individual case, but it's not a
> super-critical change in behaviour that might require testing
> in snaps.
> 
> Yours,
>   Ingo
> 
Your reasoning makes sense to me.
Assuming you're confident enough with the applications linked to
libedit: OK martijn@



Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Theo de Raadt
Ingo Schwarze  wrote:

> > So if we decide which of our interpretations should take precedence
> > it might be a good idea to put it into snaps for a while.
> 
> I don't think so in this case.  Let's not over-use the feature of
> putting stuff in snaps.  I think that should be reserved for stuff
> that is quite important and somewhat urgent and can't easily be
> tested in a less disruptive way.  But here, testing a program is
> quite feasible once you know which program to test.

We know what needs testing, because it links against the library.
Once the first set of base programs is tested to work well with the
change, it is quite likely we have complete confidence it only
improves the situation in ports.

Snapshot testing is reserved for discovering unknown breakage, quickly.
Like "we don't know how or which programs abuse something" or "what
weird machines do people have" or "we don't know how our user's fingers
work".  It's not like our users are going to quickly discover a weird
behaviour from ^C, considering I only noticed this decade-old problem
in sftp a few days ago.



Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Theo de Raadt
Ingo Schwarze  wrote:

> as mentioned earlier, deraadt@ reported that sftp(1) ignores Ctrl-C.
> Fixing that without longjmp(3) requires making editline(3) better
> behaved.

If a library interface encourages use longjmp(), that library should be
thrown into the dustbin of history.  Our src tree has very few longjmp
these days.  Thank you for the effort to discourage addition of more.

> The following patch causes el_gets(3) and el_wgets(3) to return
> failure when read(2)ing from the terminal is interrupted by a
> signal other than SIGCONT or SIGWINCH.  That allows the calling
> program to decide what to do, usually either exit the program or
> provide a fresh prompt to the user.

Looks good.

>  * bc(1)
>It behaves well with the patch: Ctrl-C discards the current
>input line and starts a new input line.
>The reason why this already works even without the patch
>is that bc(1) does very scary stuff inside the signal handler:
>pass a file-global EditLine pointer on the heap to el_line(3)
>and access fields inside the returned struct.  Needless to
>say that no signal handler should do such things...

Otto -- if you are short of time, at minimum mark the variable usage
line with /* XXX signal race */ as we have done throughout the tree.  I
would encourage anyone who sees such problems inside any signal handler
to show such comment-adding diffs.  If these problems are documented,
they can be fixed eventually, usually through event-loop logic, but I'll
admit many of the comments are in non-event-loop programs.

>  * ftp(1)
>It behaves well with the patch: Ctrl-C discards the current
>input line and provides a fresh prompt.
>The reason why it already works without the patch is that ftp(1)
>uses setjmp(3)/longjmp(3) to forcefully grab back control
>from el_gets(3) without waiting for it to return.

Horrible isn't it.

>  * sftp(1)
>Behaviour is improved with the patch: Ctrl-C now exits sftp(1).
>If desired, i can supply a very simple follow-up patch to sftp.c
>to instead behave like ftp(1) and bc(1), i.e. discard the
>current input line and provide a fresh prompt.
>Neither doing undue work in the signal handler nor longjmp(3)
>will be required for that (if this patch gets committed).

I suspect dtucker will want to decide on the interactive behaviour,
but what you describe sounds right.

> Also note that deraadt@ pointed out in private mail to me that the
> fact that read__fixio() clears FIONBIO is probably a symptom of
> botched editline(3) API design.  That might be worth fixing, too,
> as far as that is feasible, but it is unrelated to the sftp(1)
> Ctrl-C issue; let's address one topic at a time.

I mentioned rarely having seen a good outcome from code mixing any of
the 3: FIONBIO, FIONREAD, and select/poll.  Often the non-blocking was
added to select/poll code to hide some sort of bug, or the select/poll
code was added amateurishly to older code without removing the FIONBIO.
There are a few situations you need both approaches mixed, but it isn't
the general case, and thus FIONBIO has a "polled IO" smell for me.



Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Ingo Schwarze
Hi Martijn,

Martijn van Duren wrote on Mon, Aug 09, 2021 at 02:15:42PM +0200:

> If we're stripping down fixio to a single function, why not
> inline the whole thing?

I deliberately tried to:

 1. Keep patches that change behaviour as small as possible to make
review as simple as possible for people who fear they might be
affected but are not specifically interested in editline(3).

 2. Make sure that reorg / cleanup patches do not change behaviour.

> Also, as far as my brain explains things to me, it could theoretically
> be possible that the signal handler kicks in right after we return a -1
> from read(2), making it possible to get something like an EIO and
> entering the sig switch statement. Assuming that a signal handler
> doesn't clobber our errno (which libedit doesn't do, but who knows what
> bad code is out there) we should probably only check the signal type
> when we know we have an EINTR.

Yes, that looks like a race condition bug that so far escaped my
attention.  But it seems unrelated to what should happen with signals
in general, so can we keep fixing that bug separate?

> Finally, I don't understand why we only have a single retry on EAGAIN?

Not having *any* retries inside read_char() would look like a worthy
long-term goal to me, but i'm not yet completely sure that can be
reached.  I would prefer to steer into the direction of fewer magic
retries rather than more of them.  Either way, EAGAIN seems unrelated
to SIGINT, so i'd prefer to keep topics separate.

> If the application keeps resetting the FIONBIO in such a furious way
> that it happens more then once in a single call (no idea how that could
> happen) it might be an uphill battle, but we just burn away cpu cycles.
> It is not and should probably not be treated like something fatal.

Actually, if the application sets FIONBIO at all (even once), chances
are quite low in the first place that stuff works as inteded by the
author of the application.  So i certainly wouldn't worry about an
application setting FIONBIO in a loop, we have significantly worse
problems than that.  But again, that's a separate topic.

> Diff below does this. (minus 27 LoC)

I do not in general object to cleaning this code up, and getting rid
of read__fixio() indeed seems to be a long-term goal.  But i hope
we will be able to remove all the (mostly broken) functionality
from read__fixio() in a step-by-step manner, and once the function
is empty, it will fade away without having to disturb the code in
read_char().  Either way - separate topic...

The most important short-term goal seems to fix sftp(1), including
the steps required to get that done in a clean way.

> The following ports use libedit (there might be a better way of
> finding them, but this works):

Hmm, thanks, that list feels useful.

> So if we decide which of our interpretations should take precedence
> it might be a good idea to put it into snaps for a while.

I don't think so in this case.  Let's not over-use the feature of
putting stuff in snaps.  I think that should be reserved for stuff
that is quite important and somewhat urgent and can't easily be
tested in a less disruptive way.  But here, testing a program is
quite feasible once you know which program to test.

Besides, *if* this patch causes a change in behaviour of a port,
the most likely change is that a program that now ignores SIGINT
exits on SIGINT afterwards.  That may be worth investigating and
making a decision on in each individual case, but it's not a
super-critical change in behaviour that might require testing
in snaps.

Yours,
  Ingo



Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Claudio Jeker
On Mon, Aug 09, 2021 at 01:19:08PM +0200, Ingo Schwarze wrote:
> Hi,
> 
> as mentioned earlier, deraadt@ reported that sftp(1) ignores Ctrl-C.
> Fixing that without longjmp(3) requires making editline(3) better
> behaved.
> 
> Currently, when read(2) from the terminal gets interrupted by a
> signal, editline(3) ignores the (first) signal and unconditionally
> calls read(2) a second time.  That seems wrong: if the user hits
> Ctrl-C, it is sane to assume that they meant it, not that they
> want it ignored.
> 
> The following patch causes el_gets(3) and el_wgets(3) to return
> failure when read(2)ing from the terminal is interrupted by a
> signal other than SIGCONT or SIGWINCH.  That allows the calling
> program to decide what to do, usually either exit the program or
> provide a fresh prompt to the user.
> 
> If i know how to grep(1), the following programs in the base system
> use -ledit:
> 
>  * bc(1)
>It behaves well with the patch: Ctrl-C discards the current
>input line and starts a new input line.
>The reason why this already works even without the patch
>is that bc(1) does very scary stuff inside the signal handler:
>pass a file-global EditLine pointer on the heap to el_line(3)
>and access fields inside the returned struct.  Needless to
>say that no signal handler should do such things...
> 
>  * cdio(1)
>Behaviour is acceptable and unchanged with the patch:
>Ctrl-C exits cdio(1).
> 
>  * ftp(1)
>It behaves well with the patch: Ctrl-C discards the current
>input line and provides a fresh prompt.
>The reason why it already works without the patch is that ftp(1)
>uses setjmp(3)/longjmp(3) to forcefully grab back control
>from el_gets(3) without waiting for it to return.
> 
>  * lldb(1)
>It misbehaves with or without the patch and ignores Ctrl-C.
>I freely admit that i don't feel too enthusiastic about
>debugging that beast.
> 
>  * sftp(1)
>Behaviour is improved with the patch: Ctrl-C now exits sftp(1).
>If desired, i can supply a very simple follow-up patch to sftp.c
>to instead behave like ftp(1) and bc(1), i.e. discard the
>current input line and provide a fresh prompt.
>Neither doing undue work in the signal handler nor longjmp(3)
>will be required for that (if this patch gets committed).
> 
>  * bgplgsh(8), fsdb(8)
>I have no idea how to test those.  Does anyone think that testing
>either of them would be required?

I had a quick test with bgplgsh(8), hitting Ctrl-C exits bgplgsh. Same
before with or without the diff. Not sure if anyone is actually using
bgplgsh(8) -- I never used it.

-- 
:wq Claudio



Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Martijn van Duren
On Mon, 2021-08-09 at 14:15 +0200, Martijn van Duren wrote:
> On Mon, 2021-08-09 at 13:19 +0200, Ingo Schwarze wrote:
> > Hi,
> > 
> > as mentioned earlier, deraadt@ reported that sftp(1) ignores Ctrl-C.
> > Fixing that without longjmp(3) requires making editline(3) better
> > behaved.
> > 
> > Currently, when read(2) from the terminal gets interrupted by a
> > signal, editline(3) ignores the (first) signal and unconditionally
> > calls read(2) a second time.  That seems wrong: if the user hits
> > Ctrl-C, it is sane to assume that they meant it, not that they
> > want it ignored.
> > 
> > The following patch causes el_gets(3) and el_wgets(3) to return
> > failure when read(2)ing from the terminal is interrupted by a
> > signal other than SIGCONT or SIGWINCH.  That allows the calling
> > program to decide what to do, usually either exit the program or
> > provide a fresh prompt to the user.
> > 
> > If i know how to grep(1), the following programs in the base system
> > use -ledit:
> > 
> >  * bc(1)
> >    It behaves well with the patch: Ctrl-C discards the current
> >    input line and starts a new input line.
> >    The reason why this already works even without the patch
> >    is that bc(1) does very scary stuff inside the signal handler:
> >    pass a file-global EditLine pointer on the heap to el_line(3)
> >    and access fields inside the returned struct.  Needless to
> >    say that no signal handler should do such things...
> > 
> >  * cdio(1)
> >    Behaviour is acceptable and unchanged with the patch:
> >    Ctrl-C exits cdio(1).
> > 
> >  * ftp(1)
> >    It behaves well with the patch: Ctrl-C discards the current
> >    input line and provides a fresh prompt.
> >    The reason why it already works without the patch is that ftp(1)
> >    uses setjmp(3)/longjmp(3) to forcefully grab back control
> >    from el_gets(3) without waiting for it to return.
> > 
> >  * lldb(1)
> >    It misbehaves with or without the patch and ignores Ctrl-C.
> >    I freely admit that i don't feel too enthusiastic about
> >    debugging that beast.
> > 
> >  * sftp(1)
> >    Behaviour is improved with the patch: Ctrl-C now exits sftp(1).
> >    If desired, i can supply a very simple follow-up patch to sftp.c
> >    to instead behave like ftp(1) and bc(1), i.e. discard the
> >    current input line and provide a fresh prompt.
> >    Neither doing undue work in the signal handler nor longjmp(3)
> >    will be required for that (if this patch gets committed).
> > 
> >  * bgplgsh(8), fsdb(8)
> >    I have no idea how to test those.  Does anyone think that testing
> >    either of them would be required?
> > 
> > Regarding the patch below, note that differentiating EINTR behaviour
> > by signal number is not needed because the calling code in read_char()
> > already handles SIGCONT and SIGWINCH, so those two never arrive in
> > read__fixio() in the first place.
> > 
> > Also note that deraadt@ pointed out in private mail to me that the
> > fact that read__fixio() clears FIONBIO is probably a symptom of
> > botched editline(3) API design.  That might be worth fixing, too,
> > as far as that is feasible, but it is unrelated to the sftp(1)
> > Ctrl-C issue; let's address one topic at a time.
> > 
> > Any feedback is welcome.
> > 
> > Yours,
> >   Ingo
> > 
> > P.S.
> > I decided to Cc: tech@ again because this patch might affect
> > even people who are not specifically interested in editline(3),
> > and i have no intention to cause unpleasant surprises.
> > 
> If we're stripping down fixio to a single function, why not inline the
> whole thing?
> 
> Also, as far as my brain explains things to me, it could theoretically
> be possible that the signal handler kicks in right after we return a -1
> from read(2), making it possible to get something like an EIO and
> entering the sig switch statement. Assuming that a signal handler
> doesn't clobber our errno (which libedit doesn't do, but who knows what
> bad code is out there) we should probably only check the signal type
> when we know we have an EINTR.
> 
> Finally, I don't understand why we only have a single retry on EAGAIN?
> If the application keeps resetting the FIONBIO in such a furious way
> that it happens more then once in a single call (no idea how that could
> happen) it might be an uphill battle, but we just burn away cpu cycles.
> It is not and should probably not be treated like something fatal.
> 
> Diff below does this. (minus 27 LoC)
> 
> The following ports use libedit (there might be a better way of finding
> them, but this works):
> $ find . -name Makefile | xargs grep -F '.include ' | \
> > cut -d: -f1 | while read file; do \
> > (cd $(dirname $file); make show=WANTLIB | \
> > grep -q '\' && echo $(dirname $file)); \>
> > done 2> /dev/null 
> ./devel/clang-tools-extra
> ./devel/llvm
> ./mail/dcc
> ./mail/nmh
> ./emulators/gsplus
> ./emulators/nono
> ./lang/brainfuck
> ./lang/eltclsh
> ./lang/lua/5.1
> ./lang/lua/5.2
> ./lang/lua/5.3
> ./lang/swi-prolog
> 

Re: libedit: stop ignoring SIGINT

2021-08-09 Thread Martijn van Duren
On Mon, 2021-08-09 at 13:19 +0200, Ingo Schwarze wrote:
> Hi,
> 
> as mentioned earlier, deraadt@ reported that sftp(1) ignores Ctrl-C.
> Fixing that without longjmp(3) requires making editline(3) better
> behaved.
> 
> Currently, when read(2) from the terminal gets interrupted by a
> signal, editline(3) ignores the (first) signal and unconditionally
> calls read(2) a second time.  That seems wrong: if the user hits
> Ctrl-C, it is sane to assume that they meant it, not that they
> want it ignored.
> 
> The following patch causes el_gets(3) and el_wgets(3) to return
> failure when read(2)ing from the terminal is interrupted by a
> signal other than SIGCONT or SIGWINCH.  That allows the calling
> program to decide what to do, usually either exit the program or
> provide a fresh prompt to the user.
> 
> If i know how to grep(1), the following programs in the base system
> use -ledit:
> 
>  * bc(1)
>    It behaves well with the patch: Ctrl-C discards the current
>    input line and starts a new input line.
>    The reason why this already works even without the patch
>    is that bc(1) does very scary stuff inside the signal handler:
>    pass a file-global EditLine pointer on the heap to el_line(3)
>    and access fields inside the returned struct.  Needless to
>    say that no signal handler should do such things...
> 
>  * cdio(1)
>    Behaviour is acceptable and unchanged with the patch:
>    Ctrl-C exits cdio(1).
> 
>  * ftp(1)
>    It behaves well with the patch: Ctrl-C discards the current
>    input line and provides a fresh prompt.
>    The reason why it already works without the patch is that ftp(1)
>    uses setjmp(3)/longjmp(3) to forcefully grab back control
>    from el_gets(3) without waiting for it to return.
> 
>  * lldb(1)
>    It misbehaves with or without the patch and ignores Ctrl-C.
>    I freely admit that i don't feel too enthusiastic about
>    debugging that beast.
> 
>  * sftp(1)
>    Behaviour is improved with the patch: Ctrl-C now exits sftp(1).
>    If desired, i can supply a very simple follow-up patch to sftp.c
>    to instead behave like ftp(1) and bc(1), i.e. discard the
>    current input line and provide a fresh prompt.
>    Neither doing undue work in the signal handler nor longjmp(3)
>    will be required for that (if this patch gets committed).
> 
>  * bgplgsh(8), fsdb(8)
>    I have no idea how to test those.  Does anyone think that testing
>    either of them would be required?
> 
> Regarding the patch below, note that differentiating EINTR behaviour
> by signal number is not needed because the calling code in read_char()
> already handles SIGCONT and SIGWINCH, so those two never arrive in
> read__fixio() in the first place.
> 
> Also note that deraadt@ pointed out in private mail to me that the
> fact that read__fixio() clears FIONBIO is probably a symptom of
> botched editline(3) API design.  That might be worth fixing, too,
> as far as that is feasible, but it is unrelated to the sftp(1)
> Ctrl-C issue; let's address one topic at a time.
> 
> Any feedback is welcome.
> 
> Yours,
>   Ingo
> 
> P.S.
> I decided to Cc: tech@ again because this patch might affect
> even people who are not specifically interested in editline(3),
> and i have no intention to cause unpleasant surprises.
> 
If we're stripping down fixio to a single function, why not inline the
whole thing?

Also, as far as my brain explains things to me, it could theoretically
be possible that the signal handler kicks in right after we return a -1
from read(2), making it possible to get something like an EIO and
entering the sig switch statement. Assuming that a signal handler
doesn't clobber our errno (which libedit doesn't do, but who knows what
bad code is out there) we should probably only check the signal type
when we know we have an EINTR.

Finally, I don't understand why we only have a single retry on EAGAIN?
If the application keeps resetting the FIONBIO in such a furious way
that it happens more then once in a single call (no idea how that could
happen) it might be an uphill battle, but we just burn away cpu cycles.
It is not and should probably not be treated like something fatal.

Diff below does this. (minus 27 LoC)

The following ports use libedit (there might be a better way of finding
them, but this works):
$ find . -name Makefile | xargs grep -F '.include ' | \
> cut -d: -f1 | while read file; do \
> (cd $(dirname $file); make show=WANTLIB | \
> grep -q '\' && echo $(dirname $file)); \>
> done 2> /dev/null 
./devel/clang-tools-extra
./devel/llvm
./mail/dcc
./mail/nmh
./emulators/gsplus
./emulators/nono
./lang/brainfuck
./lang/eltclsh
./lang/lua/5.1
./lang/lua/5.2
./lang/lua/5.3
./lang/swi-prolog
./math/gbc
./net/dnsdist
./net/honeyd
./net/icinga/core2
./net/knot
./net/ntp
./security/kc
./security/pivy
./shells/dash
./shells/nsh
./sysutils/ipmitool

So if we decide which of our interpretations should take precedence it
might be a good idea to put it into snaps for a while.

martijn@

Index: 

libedit: stop ignoring SIGINT

2021-08-09 Thread Ingo Schwarze
Hi,

as mentioned earlier, deraadt@ reported that sftp(1) ignores Ctrl-C.
Fixing that without longjmp(3) requires making editline(3) better
behaved.

Currently, when read(2) from the terminal gets interrupted by a
signal, editline(3) ignores the (first) signal and unconditionally
calls read(2) a second time.  That seems wrong: if the user hits
Ctrl-C, it is sane to assume that they meant it, not that they
want it ignored.

The following patch causes el_gets(3) and el_wgets(3) to return
failure when read(2)ing from the terminal is interrupted by a
signal other than SIGCONT or SIGWINCH.  That allows the calling
program to decide what to do, usually either exit the program or
provide a fresh prompt to the user.

If i know how to grep(1), the following programs in the base system
use -ledit:

 * bc(1)
   It behaves well with the patch: Ctrl-C discards the current
   input line and starts a new input line.
   The reason why this already works even without the patch
   is that bc(1) does very scary stuff inside the signal handler:
   pass a file-global EditLine pointer on the heap to el_line(3)
   and access fields inside the returned struct.  Needless to
   say that no signal handler should do such things...

 * cdio(1)
   Behaviour is acceptable and unchanged with the patch:
   Ctrl-C exits cdio(1).

 * ftp(1)
   It behaves well with the patch: Ctrl-C discards the current
   input line and provides a fresh prompt.
   The reason why it already works without the patch is that ftp(1)
   uses setjmp(3)/longjmp(3) to forcefully grab back control
   from el_gets(3) without waiting for it to return.

 * lldb(1)
   It misbehaves with or without the patch and ignores Ctrl-C.
   I freely admit that i don't feel too enthusiastic about
   debugging that beast.

 * sftp(1)
   Behaviour is improved with the patch: Ctrl-C now exits sftp(1).
   If desired, i can supply a very simple follow-up patch to sftp.c
   to instead behave like ftp(1) and bc(1), i.e. discard the
   current input line and provide a fresh prompt.
   Neither doing undue work in the signal handler nor longjmp(3)
   will be required for that (if this patch gets committed).

 * bgplgsh(8), fsdb(8)
   I have no idea how to test those.  Does anyone think that testing
   either of them would be required?

Regarding the patch below, note that differentiating EINTR behaviour
by signal number is not needed because the calling code in read_char()
already handles SIGCONT and SIGWINCH, so those two never arrive in
read__fixio() in the first place.

Also note that deraadt@ pointed out in private mail to me that the
fact that read__fixio() clears FIONBIO is probably a symptom of
botched editline(3) API design.  That might be worth fixing, too,
as far as that is feasible, but it is unrelated to the sftp(1)
Ctrl-C issue; let's address one topic at a time.

Any feedback is welcome.

Yours,
  Ingo

P.S.
I decided to Cc: tech@ again because this patch might affect
even people who are not specifically interested in editline(3),
and i have no intention to cause unpleasant surprises.


Index: read.c
===
RCS file: /cvs/src/lib/libedit/read.c,v
retrieving revision 1.45
diff -u -U15 -r1.45 read.c
--- read.c  9 Aug 2021 09:11:26 -   1.45
+++ read.c  9 Aug 2021 10:00:18 -
@@ -134,22 +134,19 @@
 
 /* read__fixio():
  * Try to recover from a read error
  */
 static int
 read__fixio(int fd, int e)
 {
int zero = 0;
 
switch (e) {
case EAGAIN:
if (ioctl(fd, FIONBIO, ) == -1)
return -1;
return 0;
 
-   case EINTR:
-   return 0;
-
default:
return -1;
}
 }