diff options
author | rwatson <rwatson@FreeBSD.org> | 2005-02-21 21:58:17 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2005-02-21 21:58:17 +0000 |
commit | 26df80bf2cb36ff3fb93ad6eef085062a90896c6 (patch) | |
tree | 485f480fc21fe953c9c79e4896f5536a96968c9f /sys/netgraph/bluetooth/socket | |
parent | bc73e0ad821773b13eec9322408d7da1c61edeb6 (diff) | |
download | FreeBSD-src-26df80bf2cb36ff3fb93ad6eef085062a90896c6.zip FreeBSD-src-26df80bf2cb36ff3fb93ad6eef085062a90896c6.tar.gz |
In the current world order, solisten() implements the state transition of
a socket from a regular socket to a listening socket able to accept new
connections. As part of this state transition, solisten() calls into the
protocol to update protocol-layer state. There were several bugs in this
implementation that could result in a race wherein a TCP SYN received
in the interval between the protocol state transition and the shortly
following socket layer transition would result in a panic in the TCP code,
as the socket would be in the TCPS_LISTEN state, but the socket would not
have the SO_ACCEPTCONN flag set.
This change does the following:
- Pushes the socket state transition from the socket layer solisten() to
to socket "library" routines called from the protocol. This permits
the socket routines to be called while holding the protocol mutexes,
preventing a race exposing the incomplete socket state transition to TCP
after the TCP state transition has completed. The check for a socket
layer state transition is performed by solisten_proto_check(), and the
actual transition is performed by solisten_proto().
- Holds the socket lock for the duration of the socket state test and set,
and over the protocol layer state transition, which is now possible as
the socket lock is acquired by the protocol layer, rather than vice
versa. This prevents additional state related races in the socket
layer.
This permits the dual transition of socket layer and protocol layer state
to occur while holding locks for both layers, making the two changes
atomic with respect to one another. Similar changes are likely require
elsewhere in the socket/protocol code.
Reported by: Peter Holm <peter@holm.cc>
Review and fixes from: emax, Antoine Brodin <antoine.brodin@laposte.net>
Philosophical head nod: gnn
Diffstat (limited to 'sys/netgraph/bluetooth/socket')
-rw-r--r-- | sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c | 27 | ||||
-rw-r--r-- | sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c | 44 |
2 files changed, 46 insertions, 25 deletions
diff --git a/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c b/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c index 5662d1d..1a25fb3 100644 --- a/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c +++ b/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c @@ -2409,13 +2409,28 @@ int ng_btsocket_l2cap_listen(struct socket *so, struct thread *td) { ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); + int error; - if (pcb == NULL) - return (EINVAL); - if (ng_btsocket_l2cap_node == NULL) - return (EINVAL); - - return ((pcb->psm == 0)? EDESTADDRREQ : 0); + SOCK_LOCK(so); + error = solisten_proto_check(so); + if (error != 0) + goto out; + if (pcb == NULL) { + error = EINVAL; + goto out; + } + if (ng_btsocket_l2cap_node == NULL) { + error = EINVAL; + goto out; + } + if (pcb->psm == 0) { + error = EDESTADDRREQ; + goto out; + } + solisten_proto(so); +out: + SOCK_UNLOCK(so); + return (error); } /* ng_btsocket_listen */ /* diff --git a/sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c b/sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c index 19a8dd6..23f72fa 100644 --- a/sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c +++ b/sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c @@ -800,6 +800,7 @@ ng_btsocket_rfcomm_listen(struct socket *so, struct thread *td) ng_btsocket_rfcomm_session_p s = NULL; struct socket *l2so = NULL; int error; + int socreate_error; if (pcb == NULL) return (EINVAL); @@ -816,14 +817,18 @@ ng_btsocket_rfcomm_listen(struct socket *so, struct thread *td) * session. */ - error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET, + socreate_error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP, td->td_ucred, td); - /* - * Look for the session in LISTENING state. There can only be one. + /* + * Transition the socket and session into the LISTENING state. Check + * for collisions first, as there can only be one. */ - mtx_lock(&ng_btsocket_rfcomm_sessions_mtx); + SOCK_LOCK(so); + error = solisten_proto_check(so); + if (error != 0) + goto out; LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next) if (s->state == NG_BTSOCKET_RFCOMM_SESSION_LISTENING) @@ -835,10 +840,9 @@ ng_btsocket_rfcomm_listen(struct socket *so, struct thread *td) * L2CAP socket. If l2so == NULL then error has the error code * from socreate() */ - if (l2so == NULL) { - mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx); - return (error); + error = socreate_error; + goto out; } /* @@ -848,21 +852,23 @@ ng_btsocket_rfcomm_listen(struct socket *so, struct thread *td) * XXX FIXME Note that currently there is no way to adjust MTU * for the default session. */ - error = ng_btsocket_rfcomm_session_create(&s, l2so, NG_HCI_BDADDR_ANY, NULL, td); - if (error != 0) { - mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx); - soclose(l2so); - - return (error); - } - } else if (l2so != NULL) - soclose(l2so); /* we don't need new L2CAP socket */ - + if (error != 0) + goto out; + l2so = NULL; + } + solisten_proto(so); +out: + SOCK_UNLOCK(so); mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx); - - return (0); + /* + * If we still have an l2so reference here, it's unneeded, so release + * it. + */ + if (l2so != NULL) + soclose(l2so); + return (error); } /* ng_btsocket_listen */ /* |