On Thu, Nov 11, 2021 at 12:09:41PM +0300, Vitaliy Makkoveev wrote:
> Can I propose to commit this diff before? It reorders soclose() to
> destroy PCB before `so_q0' and `so_q' cleanup.

OK bluhm@

> Also the tool to test the diff:

What happens when you run the tool with the current code?  Does the
kernel crash?  Are there some inconsistencies that userland could
detect?

If yes, we could convert it to a regress test.

bluhm

> #include <sys/types.h>
> #include <sys/socket.h>
> #include <sys/un.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <err.h>
> #include <pthread.h>
> #include <string.h>
> #include <unistd.h>
> 
> #include <arpa/inet.h>
> #include <netinet/in.h>
> 
> #define NTHR (10)
> #define NCONNECT (10)
> #define NACCEPT ((NTHR * NCONNECT) / 2)
> 
> static void *
> thr_conn(void *arg)
> {
>       struct sockaddr_storage *ss = arg;
>       int s[NCONNECT];
>       size_t hd = 0, tl = 0;
> 
>       while (1) {
>               if ((s[hd] = socket(ss->ss_family, SOCK_STREAM, 0)) < 0)
>                       continue;
> 
>               if (connect(s[hd], (struct sockaddr *)ss, ss->ss_len) < 0) {
>                       close(s[hd]);
>                       continue;
>               }
> 
>               if ((hd = (hd + 1) % NCONNECT) == tl) {
>                       close(s[tl]);
>                       tl = (tl + 1) % NCONNECT;
>               }
>       }
> 
>       return NULL;
> }
> 
> static void
> usage(void)
> {
>       extern char *__progname;
> 
>       fprintf(stderr, "Usage %s: unix | inet | inet6\n", __progname);
>       exit(1);
> }
> 
> int
> main(int argc, char *argv[])
> {
>       struct sockaddr_storage ss;
>       size_t i;
>       int s;
> 
>       if (argc != 2) {
>               usage();
>       }
> 
>       memset(&ss, 0, sizeof(ss));
> 
>       if (strcmp(argv[1], "unix") == 0) {
>               struct sockaddr_un *sun;
> 
>               sun = (struct sockaddr_un *)&ss;
>               sun->sun_len = sizeof(*sun);
>               sun->sun_family = AF_UNIX;
>               snprintf(sun->sun_path, sizeof(sun->sun_path) - 1,
>                       "/tmp/socket%d", getpid());
>       } else if (strcmp(argv[1], "inet") == 0) {
>               struct sockaddr_in *sin;
> 
>               sin = (struct sockaddr_in *)&ss;
>               sin->sin_len = sizeof(*sin);
>               sin->sin_family = AF_INET;
>               sin->sin_port = htons(10000+(getpid()%10000));
>               if (inet_pton(AF_INET, "127.0.0.1", &sin->sin_addr) <= 0)
>                       errx(1, "inet_pton()");
>       } else if (strcmp(argv[1], "inet6") == 0) {
>               struct sockaddr_in6 *sin6;
> 
>               sin6 = (struct sockaddr_in6 *)&ss;
>               sin6->sin6_len = sizeof(*sin6);
>               sin6->sin6_family = AF_INET6;
>               sin6->sin6_port = htons(10000+(getpid()%10000));
>               if (inet_pton(AF_INET6, "::1", &sin6->sin6_addr) <= 0)
>                       errx(1, "inet_pton()");
>       } else
>               usage();
> 
>       for (i = 0; i < NTHR; ++i) {
>               pthread_t thr;
>               int error;
> 
>               if ((error = pthread_create(&thr, NULL, thr_conn, &ss)))
>                       errc(1, error, "pthread_create()");
>       }
> 
>       while(1) {
>               int conns[NACCEPT];
> 
>               if ((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
>                       err(1, "socket()");
> 
>               if (ss.ss_family == AF_UNIX) {
>                       struct sockaddr_un *sun = (struct sockaddr_un *)&ss;
>                       unlink(sun->sun_path);
>               } else {
>                       int opt = 1;
> 
>                       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
>                           &opt, sizeof(opt)) < 0)
>                               err(1, "setsockopt()");
>               }
> 
>               if (bind(s, (struct sockaddr *)&ss, ss.ss_len) < 0)
>                       err(1, "bind()");
> 
>               if (listen(s, 10) < 0)
>                       err(1, "listen()");
> 
>               for(i=0; i < NACCEPT; ++i) {
>                       if ((conns[i] = accept(s, NULL, NULL)) < 0)
>                               err(1, "accept()");
>               }
> 
>               close(s);
> 
>               for(i=0; i < NACCEPT; ++i) {
>                       close(conns[i]);
>               }
>       }
> 
>       return 0;
> }
> 
> Index: sys/kern/uipc_socket.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/uipc_socket.c,v
> retrieving revision 1.267
> diff -u -p -r1.267 uipc_socket.c
> --- sys/kern/uipc_socket.c    24 Oct 2021 07:02:47 -0000      1.267
> +++ sys/kern/uipc_socket.c    11 Nov 2021 08:56:17 -0000
> @@ -322,19 +322,9 @@ soclose(struct socket *so, int flags)
>       s = solock(so);
>       /* Revoke async IO early. There is a final revocation in sofree(). */
>       sigio_free(&so->so_sigio);
> -     if (so->so_options & SO_ACCEPTCONN) {
> -             while ((so2 = TAILQ_FIRST(&so->so_q0)) != NULL) {
> -                     (void) soqremque(so2, 0);
> -                     (void) soabort(so2);
> -             }
> -             while ((so2 = TAILQ_FIRST(&so->so_q)) != NULL) {
> -                     (void) soqremque(so2, 1);
> -                     (void) soabort(so2);
> -             }
> -     }
> -     if (so->so_pcb == NULL)
> -             goto discard;
>       if (so->so_state & SS_ISCONNECTED) {
> +             if (so->so_pcb == NULL)
> +                     goto discard;
>               if ((so->so_state & SS_ISDISCONNECTING) == 0) {
>                       error = sodisconnect(so);
>                       if (error)
> @@ -360,6 +350,16 @@ drop:
>               error2 = (*so->so_proto->pr_detach)(so);
>               if (error == 0)
>                       error = error2;
> +     }
> +     if (so->so_options & SO_ACCEPTCONN) {
> +             while ((so2 = TAILQ_FIRST(&so->so_q0)) != NULL) {
> +                     (void) soqremque(so2, 0);
> +                     (void) soabort(so2);
> +             }
> +             while ((so2 = TAILQ_FIRST(&so->so_q)) != NULL) {
> +                     (void) soqremque(so2, 1);
> +                     (void) soabort(so2);
> +             }
>       }
>  discard:
>       if (so->so_state & SS_NOFDREF)

Reply via email to