Module Name: src Committed By: bouyer Date: Sun Apr 23 21:05:09 UTC 2017
Modified Files: src/sys/netcan [bouyer-socketcan]: can.c can_pcb.c can_pcb.h Log Message: Add locking and refcounting to canpcb. Store the canpcb in the in the mbuf tag on send instead of the socket's address. This should protect against a race where the socket cloud be closed before we get back the mbuf from the adapter's driver. To generate a diff of this commit: cvs rdiff -u -r1.1.2.12 -r1.1.2.13 src/sys/netcan/can.c cvs rdiff -u -r1.1.2.3 -r1.1.2.4 src/sys/netcan/can_pcb.c \ src/sys/netcan/can_pcb.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netcan/can.c diff -u src/sys/netcan/can.c:1.1.2.12 src/sys/netcan/can.c:1.1.2.13 --- src/sys/netcan/can.c:1.1.2.12 Thu Apr 20 17:29:10 2017 +++ src/sys/netcan/can.c Sun Apr 23 21:05:09 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: can.c,v 1.1.2.12 2017/04/20 17:29:10 bouyer Exp $ */ +/* $NetBSD: can.c,v 1.1.2.13 2017/04/23 21:05:09 bouyer Exp $ */ /*- * Copyright (c) 2003, 2017 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: can.c,v 1.1.2.12 2017/04/20 17:29:10 bouyer Exp $"); +__KERNEL_RCSID(0, "$NetBSD: can.c,v 1.1.2.13 2017/04/23 21:05:09 bouyer Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -235,7 +235,10 @@ can_output(struct mbuf *m, struct canpcb ifp->if_oerrors++; return ENOMEM; } - *(struct socket **)(sotag + 1) = canp->canp_socket; + mutex_enter(&canp->canp_mtx); + canp_ref(canp); + mutex_exit(&canp->canp_mtx); + *(struct canpcb **)(sotag + 1) = canp; m_tag_prepend(m, sotag); if (m->m_len <= ifp->if_mtu) { @@ -302,7 +305,6 @@ canintr(void) struct sockaddr_can from; struct canpcb *canp; struct m_tag *sotag; - struct socket *so; struct canpcb *sender_canp; mutex_enter(softnet_lock); @@ -319,13 +321,13 @@ canintr(void) #endif sotag = m_tag_find(m, PACKET_TAG_SO, NULL); if (sotag) { - so = *(struct socket **)(sotag + 1); - sender_canp = sotocanpcb(so); + sender_canp = *(struct canpcb **)(sotag + 1); m_tag_delete(m, sotag); + KASSERT(sender_canp != NULL); /* if the sender doesn't want loopback, don't do it */ - if (sender_canp && - (sender_canp->canp_flags & CANP_NO_LOOPBACK) != 0) { + if ((sender_canp->canp_flags & CANP_NO_LOOPBACK) != 0) { m_freem(m); + canp_unref(sender_canp); continue; } } else { @@ -340,19 +342,31 @@ canintr(void) TAILQ_FOREACH(canp, &cbtable.canpt_queue, canp_queue) { struct mbuf *mc; + mutex_enter(&canp->canp_mtx); + /* skip if we're detached */ + if (canp->canp_state == CANP_DETACHED) { + mutex_exit(&canp->canp_mtx); + continue; + } + /* don't loop back to sockets on other interfaces */ if (canp->canp_ifp != NULL && canp->canp_ifp->if_index != rcv_ifindex) { + mutex_exit(&canp->canp_mtx); continue; } /* don't loop back to myself if I don't want it */ if (canp == sender_canp && - (canp->canp_flags & CANP_RECEIVE_OWN) == 0) + (canp->canp_flags & CANP_RECEIVE_OWN) == 0) { + mutex_exit(&canp->canp_mtx); continue; + } /* skip if the accept filter doen't match this pkt */ - if (!can_pcbfilter(canp, m)) + if (!can_pcbfilter(canp, m)) { + mutex_exit(&canp->canp_mtx); continue; + } if (TAILQ_NEXT(canp, canp_queue) != NULL) { /* @@ -375,9 +389,13 @@ canintr(void) m_freem(mc); } else sorwakeup(canp->canp_socket); + mutex_exit(&canp->canp_mtx); if (m == NULL) break; } + if (sender_canp) { + canp_unref(sender_canp); + } /* If it didn't go anywhere just delete it */ if (m) { m_freem(m); @@ -492,7 +510,6 @@ can_disconnect(struct socket *so) /*soisdisconnected(so);*/ so->so_state &= ~SS_ISCONNECTED; /* XXX */ can_pcbdisconnect(canp); - can_pcbstate(canp, CANP_BOUND); /* XXX */ return 0; } @@ -877,7 +894,9 @@ can_raw_setop(struct canpcb *canp, struc int nfilters = sopt->sopt_size / sizeof(struct can_filter); if (sopt->sopt_size % sizeof(struct can_filter) != 0) return EINVAL; + mutex_enter(&canp->canp_mtx); error = can_pcbsetfilter(canp, sopt->sopt_data, nfilters); + mutex_exit(&canp->canp_mtx); break; } default: Index: src/sys/netcan/can_pcb.c diff -u src/sys/netcan/can_pcb.c:1.1.2.3 src/sys/netcan/can_pcb.c:1.1.2.4 --- src/sys/netcan/can_pcb.c:1.1.2.3 Sun Feb 5 19:44:53 2017 +++ src/sys/netcan/can_pcb.c Sun Apr 23 21:05:09 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: can_pcb.c,v 1.1.2.3 2017/02/05 19:44:53 bouyer Exp $ */ +/* $NetBSD: can_pcb.c,v 1.1.2.4 2017/04/23 21:05:09 bouyer Exp $ */ /*- * Copyright (c) 2003, 2017 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: can_pcb.c,v 1.1.2.3 2017/02/05 19:44:53 bouyer Exp $"); +__KERNEL_RCSID(0, "$NetBSD: can_pcb.c,v 1.1.2.4 2017/04/23 21:05:09 bouyer Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -106,12 +106,14 @@ can_pcballoc(struct socket *so, void *v) canp->canp_socket = so; canp->canp_filters = can_init_filter; canp->canp_nfilters = 1; + mutex_init(&canp->canp_mtx, MUTEX_DEFAULT, IPL_NET); + canp->canp_refcount = 1; so->so_pcb = canp; - s = splnet(); + mutex_enter(&canp->canp_mtx); TAILQ_INSERT_HEAD(&table->canpt_queue, canp, canp_queue); can_pcbstate(canp, CANP_ATTACHED); - splx(s); + mutex_exit(&canp->canp_mtx); return (0); } @@ -122,6 +124,7 @@ can_pcbbind(void *v, struct sockaddr_can if (scan->can_family != AF_CAN) return (EAFNOSUPPORT); + mutex_enter(&canp->canp_mtx); if (scan->can_ifindex != 0) { canp->canp_ifp = if_byindex(scan->can_ifindex); if (canp->canp_ifp == NULL) @@ -132,6 +135,7 @@ can_pcbbind(void *v, struct sockaddr_can canp->canp_socket->so_state &= ~SS_ISCONNECTED; /* XXX */ } can_pcbstate(canp, CANP_BOUND); + mutex_exit(&canp->canp_mtx); return 0; } @@ -150,8 +154,10 @@ can_pcbconnect(void *v, struct sockaddr_ if (scan->can_family != AF_CAN) return (EAFNOSUPPORT); #if 0 + mutex_enter(&canp->canp_mtx); memcpy(&canp->canp_dst, scan, sizeof(struct sockaddr_can)); can_pcbstate(canp, CANP_CONNECTED); + mutex_exit(&canp->canp_mtx); return 0; #endif return EOPNOTSUPP; @@ -162,7 +168,9 @@ can_pcbdisconnect(void *v) { struct canpcb *canp = v; - can_pcbstate(canp, CANP_BOUND); + mutex_enter(&canp->canp_mtx); + can_pcbstate(canp, CANP_DETACHED); + mutex_exit(&canp->canp_mtx); if (canp->canp_socket->so_state & SS_NOFDREF) can_pcbdetach(canp); } @@ -172,28 +180,51 @@ can_pcbdetach(void *v) { struct canpcb *canp = v; struct socket *so = canp->canp_socket; - int s; KASSERT(mutex_owned(softnet_lock)); so->so_pcb = NULL; - s = splnet(); - can_pcbstate(canp, CANP_ATTACHED); - TAILQ_REMOVE(&canp->canp_table->canpt_queue, canp, canp_queue); - splx(s); - sofree(so); /* sofree drops the lock */ + mutex_enter(&canp->canp_mtx); + can_pcbstate(canp, CANP_DETACHED); can_pcbsetfilter(canp, NULL, 0); - pool_put(&canpcb_pool, canp); + mutex_exit(&canp->canp_mtx); + TAILQ_REMOVE(&canp->canp_table->canpt_queue, canp, canp_queue); + sofree(so); /* sofree drops the softnet_lock */ + canp_unref(canp); mutex_enter(softnet_lock); } void +canp_ref(struct canpcb *canp) +{ + KASSERT(mutex_owned(&canp->canp_mtx)); + canp->canp_refcount++; +} + +void +canp_unref(struct canpcb *canp) +{ + mutex_enter(&canp->canp_mtx); + canp->canp_refcount--; + KASSERT(canp->canp_refcount >= 0); + if (canp->canp_refcount > 0) { + mutex_exit(&canp->canp_mtx); + return; + } + mutex_exit(&canp->canp_mtx); + mutex_destroy(&canp->canp_mtx); + pool_put(&canpcb_pool, canp); +} + +void can_setsockaddr(struct canpcb *canp, struct sockaddr_can *scan) { + mutex_enter(&canp->canp_mtx); memset(scan, 0, sizeof (*scan)); scan->can_family = AF_CAN; scan->can_len = sizeof(*scan); scan->can_ifindex = canp->canp_ifp->if_index; + mutex_exit(&canp->canp_mtx); } int @@ -201,6 +232,7 @@ can_pcbsetfilter(struct canpcb *canp, st { struct can_filter *newf; + KASSERT(mutex_owned(&canp->canp_mtx)); if (nfilters > 0) { newf = @@ -303,6 +335,7 @@ void can_pcbstate(struct canpcb *canp, int state) { int ifindex = canp->canp_ifp ? canp->canp_ifp->if_index : 0; + KASSERT(mutex_owned(&canp->canp_mtx)); if (canp->canp_state > CANP_ATTACHED) LIST_REMOVE(canp, canp_hash); @@ -332,6 +365,7 @@ can_pcbfilter(struct canpcb *canp, struc struct can_frame *fmp; struct can_filter *fip; + KASSERT(mutex_owned(&canp->canp_mtx)); KASSERT((m->m_flags & M_PKTHDR) != 0); KASSERT(m->m_len == m->m_pkthdr.len); Index: src/sys/netcan/can_pcb.h diff -u src/sys/netcan/can_pcb.h:1.1.2.3 src/sys/netcan/can_pcb.h:1.1.2.4 --- src/sys/netcan/can_pcb.h:1.1.2.3 Sun Feb 5 10:56:12 2017 +++ src/sys/netcan/can_pcb.h Sun Apr 23 21:05:09 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: can_pcb.h,v 1.1.2.3 2017/02/05 10:56:12 bouyer Exp $ */ +/* $NetBSD: can_pcb.h,v 1.1.2.4 2017/04/23 21:05:09 bouyer Exp $ */ /*- * Copyright (c) 2003, 2017 The NetBSD Foundation, Inc. @@ -48,6 +48,7 @@ struct canpcb { LIST_ENTRY(canpcb) canp_hash; LIST_ENTRY(canpcb) canp_lhash; TAILQ_ENTRY(canpcb) canp_queue; + kmutex_t canp_mtx; /* protect states and refcount */ int canp_state; int canp_flags; struct socket *canp_socket; /* back pointer to socket */ @@ -56,6 +57,8 @@ struct canpcb { struct canpcbtable *canp_table; struct can_filter *canp_filters; /* filter array */ int canp_nfilters; /* size of canp_filters */ + + int canp_refcount; }; LIST_HEAD(canpcbhead, canpcb); @@ -71,9 +74,10 @@ struct canpcbtable { }; /* states in canp_state: */ -#define CANP_ATTACHED 0 -#define CANP_BOUND 1 -#define CANP_CONNECTED 2 +#define CANP_DETACHED 0 +#define CANP_ATTACHED 1 +#define CANP_BOUND 2 +#define CANP_CONNECTED 3 /* flags in canp_flags: */ #define CANP_NO_LOOPBACK 0x0001 /* local loopback disabled */ @@ -100,6 +104,10 @@ void can_pcbstate(struct canpcb *, int); void can_setsockaddr(struct canpcb *, struct sockaddr_can *); int can_pcbsetfilter(struct canpcb *, struct can_filter *, int); bool can_pcbfilter(struct canpcb *, struct mbuf *); + +/* refcount management */ +void canp_ref(struct canpcb *); +void canp_unref(struct canpcb *); #endif #endif /* _NETCAN_CAN_PCB_H_ */