On 11/11/13 19:03, Max Filippov wrote:
> On Mon, Nov 11, 2013 at 8:50 PM, Eric Blake <ebl...@redhat.com> wrote:
>> Quick - identify the bug in this code (from ui/curses.c):
>>
>> static void curses_winch_handler(int signum)
>> {
>>     struct winsize {
>>         unsigned short ws_row;
>>         unsigned short ws_col;
>>         unsigned short ws_xpixel;   /* unused */
>>         unsigned short ws_ypixel;   /* unused */
>>     } ws;
>>
>>     /* terminal size changed */
>>     if (ioctl(1, TIOCGWINSZ, &ws) == -1)
> 
> An unsafe function is called in a signal. See man 7 signal,
> section 'Async-signal-safe functions'. This should be avoided.
> 
>>         return;
>>
>>     resize_term(ws.ws_row, ws.ws_col);
>>     curses_calc_pad();
>>     invalidate = 1;
>>
>>     /* some systems require this */
>>     signal(SIGWINCH, curses_winch_handler);
>> }
>>
>> Here's a hint: ioctl() can clobber errno.
> 
> I believe it cannot, at least in linux, as technically the signal
> handler is always called in a new thread, specifically created
> to only handle that signal, and errno should be thread-local.

That's incorrect (*). The handler runs on a new *stack frame*, but
inside the same thread. You can specify an alternate stack for signal
handlers to run on in advance (see SA_ONSTACK / sigaltstack()), in which
case the new stack frame will be "allocated" there. Otherwise the system
will just use the normal stack. The handler indeed runs like an
"unexpected", "out-of-the-blue" normal function call.

It is actually pretty vital that the handler is run by the specific
thread that the signal has been delivered to.

(*) Example code (not a correct/portable program due to the race on
"errno", but it does disprove the idea that errno is "protected" on Linux):

  #define _XOPEN_SOURCE 600

  #include <unistd.h>
  #include <errno.h>
  #include <signal.h>
  #include <stdio.h>

  static void ringring(int signum)
  {
    errno = -12;
  }

  int main(void)
  {
    sigaction(SIGALRM,
              &(struct sigaction){ .sa_handler = &ringring },
              NULL);
    alarm(1);

    errno = 0;
    /* pause() would clobber errno */
    while (errno == 0)
      ;

    printf("%d\n", errno);
    return 0;
  }

Thanks
Laszlo

Reply via email to