Package: libncurses5
Version: 5.4-4

[source inspection suggests upstream's 5.5 still has this bug.]

This is obviously an upstream bug, but I report it to the Debian BTS
in the first instance, for you to forward upstream or fix locally,
as you choose.

The ncurses library, as part of the setup it does in initscr(), calls
tcsetattr() (or moral equivalent) to set the TTY into non-echoing mode.
(To be exact, initscr() calls newterm() calls _nc_initscr() calls
_nc_set_tty_mode() calls GET_TTY().) Unfortunately nothing is handling
the possibility that a signal interrupts tcsetattr and causes it to
return EINTR (the correct response is to retry the call). Moreover,
newterm() ignores the return value from _nc_initscr() so the application
has no chance to find out that initialisation didn't work.

I wouldn't be surprised to find that other parts of ncurses also
failed to handle EINTR correctly; but I haven't checked.

I'm going to append a test program which demonstrates the problem.
The idea of the test program is that because we've started curses
and disabled echoing no characters should be printed in response
to typing. But if we arrange for lots of signals to be received
while we're running initscr() then sometimes the echoing isn't disabled
because the tcsetattr() returned EINTR. You might have to try
running the program multiple times to get the bug to trigger.

(I ran into this whilst developing a genuine application, where it
manifested as a very annoyingly intermittent phenomenon.)

-- Peter Maydell

===begin cursesbug.c===
/* Demonstrate curses bug. Compile with:
 *  gcc -g -Wall -o cursesbug cursesbug.c -lcurses
 *
 * Run with: reset ; ./cursesbug [no]
 * (the reset is to make sure you have a clean terminal before starting)
 * If the optional argument is specified then we don't generate all the
 * SIGALRMs.
 * When the prompt says, try typing. Nothing should be echoed but if
 * signals are flying around then there's a chance that keys will be
 * echoed. It's a race condition so you might have to try running the
 * program multiple times to trigger it.
 */

#include <curses.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <errno.h>

sig_atomic_t quitnow = 0;

void handler(int signum)
{
   if (signum == SIGTERM)
      quitnow = 1;
}

void register_handler(int sig)
{
   struct sigaction sa;
   sigset_t ss;
   int i;
   
   sa.sa_flags = 0;
   sa.sa_handler = handler;
   sigfillset(&sa.sa_mask);
   do 
   {
      i = sigaction(sig, &sa, NULL);
   } while (i == -1 && errno == EINTR);
   if (i == -1) {
      perror("sigaction failed");
      exit(1);
   }

   sigemptyset(&ss);
   sigaddset(&ss, sig);
   do 
   {
      i = sigprocmask(SIG_UNBLOCK, &ss, NULL);
   } while (i == -1 && errno == EINTR);
   if (i == -1) {
      perror("sigprocmask failed");
      exit(1);
   }
}

void start_signal_storm(void)
{
   struct itimerval p;
   int i;
   register_handler(SIGALRM);

   p.it_value.tv_sec = p.it_interval.tv_sec = 0;
   p.it_value.tv_usec = p.it_interval.tv_usec = 1;
   
   do 
   {
      i = setitimer(ITIMER_REAL, &p, NULL);
   } while (i == -1 && errno == EINTR);

   if (i == -1) {
      perror("setitimer failed");
      exit(1);
   }
}

void blockalarms(void)
{
   sigset_t set;
   int i;
   sigemptyset(&set);
   sigaddset(&set, SIGALRM);
   do 
   {
      i = sigprocmask(SIG_BLOCK, &set, NULL);
   } while (i == -1 && errno == EINTR);
   if (i == -1) {
      perror("sigprocmask failed");
      exit(1);
   }
}

int main(int argc, char **argv) 
{
   register_handler(SIGTERM);
   if (argc == 1) {
      fprintf(stderr, "Starting signal storm...\n");
      start_signal_storm();
   } else {
      fprintf(stderr, "Running without signal storm...\n");
   }
   
   if (!initscr()) {
      fprintf(stderr, "initscr failed\n");
      return 1;
   }
   if (cbreak() == ERR) {
      fprintf(stderr, "cbreak failed\n");
      return 1;
   }
   if (noecho() == ERR) {
      fprintf(stderr, "noecho failed\n");
      return 1;
   }
   if (clear() == ERR) {
      fprintf(stderr, "noecho failed\n");
      return 1;
   }
   if (addstr("Start typing now: any echoing is a bug...\n") == ERR) 
   {
      fprintf(stderr, "addstr failed\n");
      return 1;
   }
   if (refresh() == ERR) {
      fprintf(stderr, "noecho failed\n");
      return 1;
   }
   while (!quitnow) {
      /* this will get woken up by the SIGALRMs, of course */
      sleep(1);
   }
   /* get here on SIGTERM */
   blockalarms();
   if (endwin() == ERR) {
      fprintf(stderr, "endwin failed\n");
      return 1;
   }
   fprintf(stderr, "Done.\n");
   return 0;
}
===endit===


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to