Date: Sun, 13 Apr 2008 08:59:14 GMT From: Masahiro Kozuka <ma-kun@kozuka.jp> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/122710: panic occured by shutdown(2) against an one-to-one SCTP socket Message-ID: <200804130859.m3D8xEc2079894@www.freebsd.org> Resent-Message-ID: <200804130900.m3D908hH023958@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 122710 >Category: kern >Synopsis: panic occured by shutdown(2) against an one-to-one SCTP socket >Confidential: no >Severity: critical >Priority: high >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Sun Apr 13 09:00:08 UTC 2008 >Closed-Date: >Last-Modified: >Originator: Masahiro Kozuka >Release: FreeBSD 7.0-RELEASE >Organization: Kyoto University >Environment: FreeBSD 7.0-RELEASE FreeBSD 7.0-RELEASE #3: Sun Apr 13 17:22:27 JST 2008 root@:/usr/src/sys/i386/compile/SHARP i386 >Description: On FreeBSD 7.0R, when shutdown(2) called with SHUT_RD or SHUT_RDWR against an one-to-one SCTP socket, sbdrop_internel() called. However, sbdrop_internel() assumes that the receiver socket buffer is one for not SCTP. In the case of SCTP, sb_mb is always null (not used). So, sbdrop_internel() will panic at sys/kern/uipc_sockbuf.c[849]. >How-To-Repeat: The below very simple codes will trigger panic .... == % cat > client.c #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <err.h> int main(int argc, char **argv) { int sfd; struct sockaddr_in sin; sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP); if (sfd < 0) { err(1, "socket"); } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); sin.sin_port = htons(10001); if (connect(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { err(1, "connect"); } write(sfd, "foo", sizeof("foo")); close(sfd); return 0; } % gcc -o client client.c % cat > server.c #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <err.h> int main(int argc, char **argv) { int sfd, sfd1; struct sockaddr_in sin, addr; socklen_t addrlen; char buf[4096]; int len; sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP); if (sfd < 0) { err(1, "socket"); } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); sin.sin_port = htons(10001); if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { err(1, "bind"); } if (listen(sfd, 1) < 0) { err(1, "listen"); } addrlen = sizeof(addr); sfd1 = accept(sfd, (struct sockaddr *)&addr, &addrlen); if (sfd1 < 0) { err(1, "accept"); } sleep(1); if (shutdown(sfd1, SHUT_RD) < 0) { err(1, "shutdown"); } len = read(sfd1, buf, sizeof(buf)); if (len < 0) { err(1, "read"); } printf("len=%d\n", len); close(sfd1); close(sfd); return 0; } % gcc -o server server.c % ./server& % ./client == >Fix: The below patch is for FreeBSD 7.0-RELEASE. diff -u -r sys.orig/kern/uipc_domain.c sys/kern/uipc_domain.c --- sys.orig/kern/uipc_domain.c 2007-08-06 23:26:00.000000000 +0900 +++ sys/kern/uipc_domain.c 2008-04-13 17:38:06.000000000 +0900 @@ -98,6 +98,7 @@ .pru_sosend = pru_sosend_notsupp, .pru_soreceive = pru_soreceive_notsupp, .pru_sopoll = pru_sopoll_notsupp, + .pru_soshutdown = pru_soshutdown_notsupp, }; static void @@ -122,6 +123,7 @@ DEFAULT(pu->pru_sosend, sosend_generic); DEFAULT(pu->pru_soreceive, soreceive_generic); DEFAULT(pu->pru_sopoll, sopoll_generic); + DEFAULT(pu->pru_soshutdown, soshutdown_generic); #undef DEFAULT if (pr->pr_init) (*pr->pr_init)(); diff -u -r sys.orig/kern/uipc_socket.c sys/kern/uipc_socket.c --- sys.orig/kern/uipc_socket.c 2008-02-02 21:44:13.000000000 +0900 +++ sys/kern/uipc_socket.c 2008-04-13 17:38:06.000000000 +0900 @@ -1857,6 +1857,17 @@ int soshutdown(struct socket *so, int how) { + + /* XXXRW: Temporary debugging. */ + KASSERT(so->so_proto->pr_usrreqs->pru_soshutdown != soshutdown, + ("soshutdown: protocol calls soshutdown")); + + return (so->so_proto->pr_usrreqs->pru_soshutdown(so, how)); +} + +int +soshutdown_generic(struct socket *so, int how) +{ struct protosw *pr = so->so_proto; if (!(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR)) @@ -2678,6 +2689,13 @@ return EOPNOTSUPP; } +int +pru_soshutdown_notsupp(struct socket *so, int how) +{ + + return EOPNOTSUPP; +} + static void filt_sordetach(struct knote *kn) { diff -u -r sys.orig/netinet/sctp_usrreq.c sys/netinet/sctp_usrreq.c --- sys.orig/netinet/sctp_usrreq.c 2007-12-10 05:23:47.000000000 +0900 +++ sys/netinet/sctp_usrreq.c 2008-04-13 17:38:21.000000000 +0900 @@ -4440,5 +4440,6 @@ .pru_shutdown = sctp_shutdown, .pru_sockaddr = sctp_ingetaddr, .pru_sosend = sctp_sosend, - .pru_soreceive = sctp_soreceive + .pru_soreceive = sctp_soreceive, + .pru_soshutdown = sctp_soshutdown }; diff -u -r sys.orig/netinet/sctputil.c sys/netinet/sctputil.c --- sys.orig/netinet/sctputil.c 2008-02-02 21:44:13.000000000 +0900 +++ sys/netinet/sctputil.c 2008-04-13 17:38:21.000000000 +0900 @@ -6065,6 +6065,48 @@ } +int +sctp_soshutdown(struct socket *so, int how) +{ + struct protosw *pr = so->so_proto; + struct sctp_inpcb *inp; + struct sctp_queued_to_read *sq; + + inp = (struct sctp_inpcb *)so->so_pcb; + if (inp == NULL) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); + return (EINVAL); + } + + if (!(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR)) + return (EINVAL); + + if (how != SHUT_WR) { + socantrcvmore(so); + + SCTP_INP_READ_LOCK(inp); + while ((sq = TAILQ_FIRST(&inp->read_queue)) != NULL) { + if (sq->length) + SCTP_STAT_INCR(sctps_left_abandon); + + TAILQ_REMOVE(&inp->read_queue, sq, next); + sctp_free_remote_addr(sq->whoFrom); + if (sq->data) { + sctp_sbfree(sq, sq->stcb, &so->so_rcv, sq->data); + sctp_m_freem(sq->data); + sq->data = NULL; + } + SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_readq, sq); + SCTP_DECR_READQ_COUNT(); + } + SCTP_INP_READ_UNLOCK(inp); + } + if (how != SHUT_RD) + return ((*pr->pr_usrreqs->pru_shutdown)(so)); + return (0); +} + + diff -u -r sys.orig/netinet/sctputil.h sys/netinet/sctputil.h --- sys.orig/netinet/sctputil.h 2007-11-06 11:48:04.000000000 +0900 +++ sys/netinet/sctputil.h 2008-04-13 17:38:21.000000000 +0900 @@ -334,6 +334,10 @@ int *flag); +int +sctp_soshutdown(struct socket *so, int how); + + void sctp_misc_ints(uint8_t from, uint32_t a, uint32_t b, uint32_t c, uint32_t d); diff -u -r sys.orig/sys/protosw.h sys/sys/protosw.h --- sys.orig/sys/protosw.h 2006-07-25 00:20:08.000000000 +0900 +++ sys/sys/protosw.h 2008-04-13 17:38:41.000000000 +0900 @@ -236,6 +236,7 @@ int *flagsp); int (*pru_sopoll)(struct socket *so, int events, struct ucred *cred, struct thread *td); + int (*pru_soshutdown)(struct socket *so, int how); void (*pru_sosetlabel)(struct socket *so); void (*pru_close)(struct socket *so); }; @@ -270,6 +271,7 @@ int *flagsp); int pru_sopoll_notsupp(struct socket *so, int events, struct ucred *cred, struct thread *td); +int pru_soshutdown_notsupp(struct socket *so, int how); #endif /* _KERNEL */ diff -u -r sys.orig/sys/socketvar.h sys/sys/socketvar.h --- sys.orig/sys/socketvar.h 2008-02-02 21:44:14.000000000 +0900 +++ sys/sys/socketvar.h 2008-04-13 17:38:41.000000000 +0900 @@ -551,6 +551,7 @@ int flags, struct thread *td); int sosetopt(struct socket *so, struct sockopt *sopt); int soshutdown(struct socket *so, int how); +int soshutdown_generic(struct socket *so, int how); void sotoxsocket(struct socket *so, struct xsocket *xso); void sowakeup(struct socket *so, struct sockbuf *sb); >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200804130859.m3D8xEc2079894>