kern/122710: panic occured by shutdown(2) against an one-to-one
SCTP socket
Masahiro Kozuka
ma-kun at kozuka.jp
Sun Apr 13 09:00:09 UTC 2008
>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:
More information about the freebsd-bugs
mailing list