Subject: kern/31572: [dM] tty MIN>1 TIME>0 O_NONBLOCK spins
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: der Mouse <mouse@Rodents.Montreal.QC.CA>
List: netbsd-bugs
Date: 10/13/2005 03:59:01
>Number:         31572
>Category:       kern
>Synopsis:       When MIN>1 TIME>0, poll() on tty succeeds but read EAGAINs
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Oct 13 03:59:00 +0000 2005
>Originator:     der Mouse
>Release:        NetBSD 2.0, also present in 1.4T, probably others
>Organization:
	Dis-
>Environment:
	First seen on 1.4T sparc.  Tested present on 2.0 i386.  Code
	inspection makes me suspect it's still present in -current.
>Description:
	On a tty (tested only on pseudo-ttys, but code inspection makes
	me think it applies to all ttys), when c_cc[VMIN] > 1 and
	c_cc[VTIME] > 0 (and ICANON is off), and non-blocking I/O is
	turned on, then poll() returns indicating readable as soon as
	one character is typed, but a read returns showing EWOULDBLOCK
	until VMIN characters have been received.  (Presumably if
	non-blocking I/O were turned off, read would block instead,
	which is also wrong but not quite as noisily so.)

	I've marked this as serious/low, serious because it is fairly
	serious for poll and read to disagree like this, but low
	because it obviously doesn't break much or it would have been
	found and fixed long ago.
>How-To-Repeat:
	Try the above conditions.  Here's a test program.  Compile it
	(no particular options needed for me on 2.0 i386 or 1.4T sparc)
	and run it with no arguments.  Type a character and watch the
	flood of dots (type a second character to end it; interrupting
	it may leave your tty state hosed).

#include <poll.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <strings.h>

extern const char *__progname;

static struct termios tio_o;
static struct termios tio_n;

static void restore(void)
{
 tcsetattr(0,TCSANOW,&tio_o);
 fcntl(0,F_SETFL,fcntl(0,F_GETFL,0)&~O_NONBLOCK);
}

int main(void);
int main(void)
{
 int i;
 int e;
 char ch[2];

 if (tcgetattr(0,&tio_o) < 0)
  { fprintf(stderr,"%s: tcgetattr: %s\n",__progname,strerror(errno));
    exit(1);
  }
 tio_n = tio_o;
 cfmakeraw(&tio_n);
 tio_n.c_cc[VMIN] = 2;
 tio_n.c_cc[VTIME] = 1;
 fcntl(0,F_SETFL,fcntl(0,F_GETFL,0)|O_NONBLOCK);
 printf("\
Test ends as soon as you type two characters.  Each dot printed indicates\n\
poll() succeeded but read() returned EWOULDBLOCK (this shouldn't happen).\n");
 tcsetattr(0,TCSADRAIN,&tio_n);
 while (1)
  { struct pollfd pfd;
    pfd.fd = 0;
    pfd.events = POLLIN | POLLRDNORM;
    i = poll(&pfd,1,INFTIM);
    if (i < 0)
     { switch (errno)
	{ case EWOULDBLOCK: case EINTR: continue; break;
	}
       e = errno;
       restore();
       fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno));
       exit(1);
     }
    i = read(0,&ch[0],2);
    if (i < 0)
     { switch (errno)
	{ case EWOULDBLOCK:
	     write(1,".",1);
	     continue;
	     break;
	  case EINTR:
	     continue;
	     break;
	}
       e = errno;
       restore();
       fprintf(stderr,"%s: read: %s\n",__progname,strerror(errno));
       exit(1);
     }
    if (i != 2)
     { restore();
       fprintf(stderr,"%s: read: wanted 2, got %d\n",__progname,i);
       exit(1);
     }
    restore();
    exit(0);
  }
}

>Fix:
	Workaround: when doing raw-style I/O, always set c_cc[VMIN] to
	1 and/or c_cc[VTIME] to 0.  (These should always be set to
	something; values in violation of this are not unreasonable
	when doing time-limited record-style input.)

	Fix: I speculate that ttpoll() needs a check more complicated
	than "ttnread(tp) > 0" in the POLLIN|POLLRDNORM case, so it
	doesn't misfire in cases when ttread() would sleep.  However, I
	have not even designed such a check, much less tested one, so
	this is speculation.

/~\ The ASCII				der Mouse
\ / Ribbon Campaign
 X  Against HTML	       mouse@rodents.montreal.qc.ca
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B