On 20/01/17(Fri) 22:25, Alexander Bluhm wrote:
> On Thu, Jan 19, 2017 at 07:14:59PM +1000, Martin Pieuchot wrote:
> > Index: kern/uipc_syscalls.c
> > ===================================================================
> > RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v
> > retrieving revision 1.144
> > diff -u -p -r1.144 uipc_syscalls.c
> > --- kern/uipc_syscalls.c    29 Dec 2016 12:12:43 -0000      1.144
> > +++ kern/uipc_syscalls.c    19 Jan 2017 09:03:56 -0000
> > @@ -278,6 +278,7 @@ doaccept(struct proc *p, int sock, struc
> >  
> >     headfp = fp;
> >  redo:
> > +   fdplock(fdp);
> >     NET_LOCK(s);
> >     head = headfp->f_data;
> >     if (isdnssocket(head) || (head->so_options & SO_ACCEPTCONN) == 0) {
> ...
> 
> There we have
>                 error = rwsleep(&head->so_timeo, &netlock, PSOCK | PCATCH,
>                     "netcon", 0);
> 
> You release the netlock during sleep but still hold the fdplock.
> This leads to a deadlock of userland during
> /usr/src/regress/lib/libpthread/socket/1.
> 
>  277134  socket1           native    0xffff800003accfd8  thrsleep
>  236121  socket1           native    0xffffff00023eab56  netcon
>  220469  socket1           native    0xffffff0004d53a98  fdlock
> 
> ddb{0}> trace /p 0t236121
> sleep_finish() at sleep_finish+0xb1
> rwsleep() at rwsleep+0x8e
> doaccept() at doaccept+0x37b
> syscall() at syscall+0x27b
> --- syscall (number 30) ---
> 
> ddb{0}> trace /p 0t220469
> sleep_finish() at sleep_finish+0xb1
> rw_enter() at rw_enter+0x1cb
> sys_socket() at sys_socket+0xbf
> syscall() at syscall+0x27b
> --- syscall (number 97) ---

Here's another way to fix the problem, call falloc() before grabbing the
NET_LOCK().

Comments?

Index: kern/uipc_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.144
diff -u -p -r1.144 uipc_syscalls.c
--- kern/uipc_syscalls.c        29 Dec 2016 12:12:43 -0000      1.144
+++ kern/uipc_syscalls.c        21 Jan 2017 00:38:18 -0000
@@ -265,7 +265,7 @@ doaccept(struct proc *p, int sock, struc
 {
        struct filedesc *fdp = p->p_fd;
        struct file *fp, *headfp;
-       struct mbuf *nam;
+       struct mbuf *nam = NULL;
        socklen_t namelen;
        int error, s, tmpfd;
        struct socket *head, *so;
@@ -277,19 +277,35 @@ doaccept(struct proc *p, int sock, struc
                return (error);
 
        headfp = fp;
+       head = headfp->f_data;
+
+       fdplock(fdp);
+       error = falloc(p, &fp, &tmpfd);
+       if (error == 0 && (flags & SOCK_CLOEXEC))
+               fdp->fd_ofileflags[tmpfd] |= UF_EXCLOSE;
+       fdpunlock(fdp);
+       if (error != 0) {
+               /*
+                * Probably ran out of file descriptors.  Wakeup
+                * so some other process might have a chance at it.
+                */
+               wakeup_one(&head->so_timeo);
+               FRELE(headfp, p);
+               return (error);
+       }
+
 redo:
        NET_LOCK(s);
-       head = headfp->f_data;
        if (isdnssocket(head) || (head->so_options & SO_ACCEPTCONN) == 0) {
                error = EINVAL;
-               goto bad;
+               goto out;
        }
        if ((head->so_state & SS_NBIO) && head->so_qlen == 0) {
                if (head->so_state & SS_CANTRCVMORE)
                        error = ECONNABORTED;
                else
                        error = EWOULDBLOCK;
-               goto bad;
+               goto out;
        }
        while (head->so_qlen == 0 && head->so_error == 0) {
                if (head->so_state & SS_CANTRCVMORE) {
@@ -298,34 +314,19 @@ redo:
                }
                error = tsleep(&head->so_timeo, PSOCK | PCATCH,
                    "netcon", 0);
-               if (error) {
-                       goto bad;
-               }
+               if (error)
+                       goto out;
        }
        if (head->so_error) {
                error = head->so_error;
                head->so_error = 0;
-               goto bad;
+               goto out;
        }
 
        /* Figure out whether the new socket should be non-blocking. */
        nflag = flags & SOCK_NONBLOCK_INHERIT ? (headfp->f_flag & FNONBLOCK)
            : (flags & SOCK_NONBLOCK ? FNONBLOCK : 0);
 
-       fdplock(fdp);
-       error = falloc(p, &fp, &tmpfd);
-       if (error == 0 && (flags & SOCK_CLOEXEC))
-               fdp->fd_ofileflags[tmpfd] |= UF_EXCLOSE;
-       fdpunlock(fdp);
-       if (error != 0) {
-               /*
-                * Probably ran out of file descriptors.  Wakeup
-                * so some other process might have a chance at it.
-                */
-               wakeup_one(&head->so_timeo);
-               goto bad;
-       }
-
        nam = m_get(M_WAIT, MT_SONAME);
 
        /*
@@ -360,24 +361,20 @@ redo:
        error = soaccept(so, nam);
        if (!error && name != NULL)
                error = copyaddrout(p, nam, name, namelen, anamelen);
-
-       if (error) {
-               /* if an error occurred, free the file descriptor */
-               NET_UNLOCK(s);
-               m_freem(nam);
+       if (!error) {
+               (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&nflag, p);
+               FILE_SET_MATURE(fp, p);
+               *retval = tmpfd;
+       }
+out:
+       NET_UNLOCK(s);
+       m_freem(nam);
+       if (error != 0) {
                fdplock(fdp);
                fdremove(fdp, tmpfd);
                closef(fp, p);
                fdpunlock(fdp);
-               goto out;
        }
-       (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&nflag, p);
-       FILE_SET_MATURE(fp, p);
-       *retval = tmpfd;
-       m_freem(nam);
-bad:
-       NET_UNLOCK(s);
-out:
        FRELE(headfp, p);
        return (error);
 }

Reply via email to