aboutsummaryrefslogtreecommitdiffstats
path: root/net/l2tp
diff options
context:
space:
mode:
authorTom Parkin <tparkin@katalix.com>2013-01-22 00:13:48 -0500
committerDavid S. Miller <davem@davemloft.net>2013-01-29 15:43:02 -0500
commit80d84ef3ff1ddc7a829c58980a9dd566a8af5203 (patch)
tree115869b9fccce4bb2ecc7dd5a0ec2ba203802929 /net/l2tp
parentfc16e884a2320198b8cb7bc2fdcf6b4485e79709 (diff)
l2tp: prevent l2tp_tunnel_delete racing with userspace close
If a tunnel socket is created by userspace, l2tp hooks the socket destructor in order to clean up resources if userspace closes the socket or crashes. It also caches a pointer to the struct sock for use in the data path and in the netlink interface. While it is safe to use the cached sock pointer in the data path, where the skb references keep the socket alive, it is not safe to use it elsewhere as such access introduces a race with userspace closing the socket. In particular, l2tp_tunnel_delete is prone to oopsing if a multithreaded userspace application closes a socket at the same time as sending a netlink delete command for the tunnel. This patch fixes this oops by forcing l2tp_tunnel_delete to explicitly look up a tunnel socket held by userspace using sockfd_lookup(). Signed-off-by: Tom Parkin <tparkin@katalix.com> Signed-off-by: James Chapman <jchapman@katalix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/l2tp')
-rw-r--r--net/l2tp/l2tp_core.c76
-rw-r--r--net/l2tp/l2tp_core.h5
2 files changed, 69 insertions, 12 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 1a9f3723c13c..06389d5ff120 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -168,6 +168,51 @@ l2tp_session_id_hash_2(struct l2tp_net *pn, u32 session_id)
168 168
169} 169}
170 170
171/* Lookup the tunnel socket, possibly involving the fs code if the socket is
172 * owned by userspace. A struct sock returned from this function must be
173 * released using l2tp_tunnel_sock_put once you're done with it.
174 */
175struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel)
176{
177 int err = 0;
178 struct socket *sock = NULL;
179 struct sock *sk = NULL;
180
181 if (!tunnel)
182 goto out;
183
184 if (tunnel->fd >= 0) {
185 /* Socket is owned by userspace, who might be in the process
186 * of closing it. Look the socket up using the fd to ensure
187 * consistency.
188 */
189 sock = sockfd_lookup(tunnel->fd, &err);
190 if (sock)
191 sk = sock->sk;
192 } else {
193 /* Socket is owned by kernelspace */
194 sk = tunnel->sock;
195 }
196
197out:
198 return sk;
199}
200EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_lookup);
201
202/* Drop a reference to a tunnel socket obtained via. l2tp_tunnel_sock_put */
203void l2tp_tunnel_sock_put(struct sock *sk)
204{
205 struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk);
206 if (tunnel) {
207 if (tunnel->fd >= 0) {
208 /* Socket is owned by userspace */
209 sockfd_put(sk->sk_socket);
210 }
211 sock_put(sk);
212 }
213}
214EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_put);
215
171/* Lookup a session by id in the global session list 216/* Lookup a session by id in the global session list
172 */ 217 */
173static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id) 218static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id)
@@ -1607,6 +1652,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
1607 tunnel->old_sk_destruct = sk->sk_destruct; 1652 tunnel->old_sk_destruct = sk->sk_destruct;
1608 sk->sk_destruct = &l2tp_tunnel_destruct; 1653 sk->sk_destruct = &l2tp_tunnel_destruct;
1609 tunnel->sock = sk; 1654 tunnel->sock = sk;
1655 tunnel->fd = fd;
1610 lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock"); 1656 lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock");
1611 1657
1612 sk->sk_allocation = GFP_ATOMIC; 1658 sk->sk_allocation = GFP_ATOMIC;
@@ -1642,24 +1688,32 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create);
1642 */ 1688 */
1643int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) 1689int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel)
1644{ 1690{
1645 int err = 0; 1691 int err = -EBADF;
1646 struct socket *sock = tunnel->sock ? tunnel->sock->sk_socket : NULL; 1692 struct socket *sock = NULL;
1693 struct sock *sk = NULL;
1694
1695 sk = l2tp_tunnel_sock_lookup(tunnel);
1696 if (!sk)
1697 goto out;
1698
1699 sock = sk->sk_socket;
1700 BUG_ON(!sock);
1647 1701
1648 /* Force the tunnel socket to close. This will eventually 1702 /* Force the tunnel socket to close. This will eventually
1649 * cause the tunnel to be deleted via the normal socket close 1703 * cause the tunnel to be deleted via the normal socket close
1650 * mechanisms when userspace closes the tunnel socket. 1704 * mechanisms when userspace closes the tunnel socket.
1651 */ 1705 */
1652 if (sock != NULL) { 1706 err = inet_shutdown(sock, 2);
1653 err = inet_shutdown(sock, 2);
1654 1707
1655 /* If the tunnel's socket was created by the kernel, 1708 /* If the tunnel's socket was created by the kernel,
1656 * close the socket here since the socket was not 1709 * close the socket here since the socket was not
1657 * created by userspace. 1710 * created by userspace.
1658 */ 1711 */
1659 if (sock->file == NULL) 1712 if (sock->file == NULL)
1660 err = inet_release(sock); 1713 err = inet_release(sock);
1661 }
1662 1714
1715 l2tp_tunnel_sock_put(sk);
1716out:
1663 return err; 1717 return err;
1664} 1718}
1665EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); 1719EXPORT_SYMBOL_GPL(l2tp_tunnel_delete);
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 56d583e083a7..e62204cad4fe 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -188,7 +188,8 @@ struct l2tp_tunnel {
188 int (*recv_payload_hook)(struct sk_buff *skb); 188 int (*recv_payload_hook)(struct sk_buff *skb);
189 void (*old_sk_destruct)(struct sock *); 189 void (*old_sk_destruct)(struct sock *);
190 struct sock *sock; /* Parent socket */ 190 struct sock *sock; /* Parent socket */
191 int fd; 191 int fd; /* Parent fd, if tunnel socket
192 * was created by userspace */
192 193
193 uint8_t priv[0]; /* private data */ 194 uint8_t priv[0]; /* private data */
194}; 195};
@@ -228,6 +229,8 @@ out:
228 return tunnel; 229 return tunnel;
229} 230}
230 231
232extern struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel);
233extern void l2tp_tunnel_sock_put(struct sock *sk);
231extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id); 234extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id);
232extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); 235extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
233extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); 236extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);