[Bug 3547] shell read is maybe too safe

Rich Felker dalias at aerifal.cx
Mon May 9 23:27:57 UTC 2011


On Tue, May 10, 2011 at 01:09:30AM +0200, Laurent Bercot wrote:
> > There is no reason for a correct program to ever check for EINTR
> > unless it specifically installs signal handlers and fails to specify
> > SA_RESTART for them. Checking for ret==-1&&errno==EINTR in your read
> > loop is purely bloat that does not belong in most programs.
> 
>  Well, I disagree, and I'll keep disagreeing until there's a release of
> the Single Unix specification that says explicitly that the default
> behaviour for every signal that does not terminate the process MUST be
> SA_RESTART.

The default action for a signal is either ignoring it or stopping or
terminating the process. Ignoring it obviously does not cause EINTR,
and for terminating the process it's irrelevant since no further code
is executed. This leaves only stopping. I'm uncertain if syscalls
interrupted by SIGSTOP are required to be restarted, but I'd certainly
consider restarting them a quality-of-implementation requirement for
any implementation I wanted to use..

>  I agree with Denys that it would probably be the best fix. But until
> that day, color me paranoid, I'm keeping my safe wrappers.

And I'm telling users to file a bug report with their OS vendor if
EINTR happens when the program didn't explicitly install a
non-SA_RESTART signal handler..

>  And how do you manage the fd that has to be kept open to read the
> script, when the script is in a file ? Do you read all the script into
> memory then parse it from there ? Or do you somehow make an exception ?
> If you're making an exception for one fd, you might as well allow
> yourself more leverage and reserve a range of private descriptors.

I think the solution, as I realized later, is to dynamically reassign
your file descriptors not to clash with those used by the script
(using dup). And of course make them close-on-exec so external
commands never see them.

>  No. I'm thinking of poll() and asynchronous handling of stdin,
> signals (via a selfpipe or signalfd) and other events I'm not thinking of
> right off the bat. If you're only allowed to listen to stdin and no other
> single fd, there's not much you can do asynchronously - not with the poll()
> model anyway.

Here are a few solutions:

If you only unblock signals during read (which is async-signal-safe)
then you're free to call whatever functions you want from the signal
handler itself. Even returning from the signal handler with longjmp
should be safe (it's never explicitly declared unsafe, as far as I
know).

The same applies if you're using poll too, as long as you avoid
async-signal-unsafe functions in the code that runs with signals
unmasked.

Or...

> > This is what pselect() is for.
> 
>  pselect() is a horrible API, but I agree that in this case it could be
> useful. You really have to hate open descriptors to use it instead of a
> selfpipe, though.

The only bad things about pselect are those it shares with select,
i.e. fd_set having a fixed size limit. Otherwise it's very nice.

> > Or you could use threads. :-)
> 
>  Yes, you could. And triple the complexity of your shell. This is busybox
> we're talking about, again, not GNU bash ;)

Actually thread may let you reduce the complexity. Most things that
are complex without threads become easy with threads. But I'm quite
aware that busybox is allergic to them. :)

On the other hand, the file-descriptor shuffling issue could be one
thing that makes threads a really bad solution for a shell. If you
treat open files as an abstract resource where the fd number is like a
pointer, then threads are no problem, but if fd number assignment must
be meaningful, you'd have to synchronize all operations that allocate
and remap fds...

Rich


More information about the busybox mailing list