Subject: kern/21030: fcntl(2) to accepted socket does not work correctly
To: None <gnats-bugs@gnats.netbsd.org>
From: Naoto Shimazaki <igy@arhc.org>
List: netbsd-bugs
Date: 04/05/2003 23:48:39
>Number:         21030
>Category:       kern
>Synopsis:       fcntl(2) to accepted socket does not work correctly
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Apr 05 06:49:01 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     
>Release:        NetBSD 1.6Q
>Organization:
PRUG
>Environment:
System: NetBSD caolila 1.6Q NetBSD 1.6Q (CAOLILA) #5: Sat Apr 5 22:50:32 JST 2003 igy@caolila:/net/cur/obj/sys/arch/i386/compile/CAOLILA i386

Architecture: i386
Machine: i386

>Description:
Inconsistency between f_flag in struct file and so_state in struct socket
breaks latest revision (1.107) of fcntl(2).

1. create a socket and set the flag "O_NONBLOCK".
2. doing bind(2), listen(2), and accept(2)

Under this condition, new socket provided by accept(2) behaves as
non-blocking I/O because SS_NBIO in so_state is inherited from the
listening socket.  However fcntl(sk, F_GETFL) says O_NONBLOCK flag is
cleared.

There is inconsistency between f_flag and so_state.

When do fcntl(sk, F_SETFL, 0) on the new socket with latest fcntl(2),
fcntl(2) skips (*fp->f_ops->fo_ioctl)(fp, FIONBIO, &fl, p) because
O_NONBLOCK is already cleared.  the new socket still behaves as
non-blocking I/O.

This could happen with O_ASYNC.


>How-To-Repeat:
Run the program attached below.

I tried it on a few system.  Here is the result.

lsk: listening socket
ask: socket returned by accept(2)

1. Listening without O_NONBLOCK
			   flag
			lsk	ask	read
NetBSD-current		0x002	0x002	blocked
NetBSD-1.6		0x002	0x002	blocked
FreeBSD-4.7		0x002	0x002	blocked
Linux-2.4.20		0x002	0x002	blocked
NetBSD-current(patched)	0x002	0x002	blocked

2. Listening with O_NONBLOCK
			   flag
			lsk	ask	read
NetBSD-current		0x006	0x002	EAGAIN	<- flag is inconsistent
NetBSD-1.6		0x006	0x002	EAGAIN	<- flag is inconsistent
FreeBSD-4.7		0x006	0x006	EAGAIN
Linux-2.4.20		0x802	0x002	blocked
NetBSD-current(patched)	0x006	0x006	EAGAIN

3. Listening with O_NONBLOCK,
   do fcntl(ask, F_SETFL, 0) for accepted socket
			   flag
			lsk	ask	read
NetBSD-currentr		0x006	0x002	EAGAIN	<- fcntl is ignored
NetBSD-1.6		0x006	0x002	blocked
FreeBSD-4.7		0x006	0x002	blocked
Linux-2.4.20		0x802	0x002	blocked
NetBSD-current(patched)	0x006	0x002	blocked

P.S.
I found Linux does not inherit blocking behavior :-)


>Fix:
apply this patch

-------- cut here --------
--- uipc_syscalls.c	2003-04-05 22:57:42.000000000 +0900
+++ uipc_syscalls.c.new	2003-04-05 22:57:47.000000000 +0900
@@ -175,6 +175,7 @@
 	unsigned int	namelen;
 	int		error, s, fd;
 	struct socket	*so;
+	int		fflag;
 
 	p = l->l_proc;
 	fdp = p->p_fd;
@@ -222,6 +223,7 @@
 		splx(s);
 		return (error);
 	}
+	fflag = fp->f_flag;
 	/* falloc() will use the descriptor for us */
 	if ((error = falloc(p, &fp, &fd)) != 0) {
 		splx(s);
@@ -238,7 +240,7 @@
 	  so = aso;
 	}
 	fp->f_type = DTYPE_SOCKET;
-	fp->f_flag = FREAD|FWRITE;
+	fp->f_flag = fflag;
 	fp->f_ops = &socketops;
 	fp->f_data = (caddr_t)so;
 	FILE_UNUSE(fp, p);
-------- cut here --------

--
Naoto Shimazaki
>Release-Note:
>Audit-Trail:
>Unformatted: