diff options
| author | James Chapman <jchapman@katalix.com> | 2012-05-29 19:13:23 -0400 |
|---|---|---|
| committer | Luis Henriques <luis.henriques@canonical.com> | 2012-07-03 11:29:03 -0400 |
| commit | 37b639401f594ea9f1eb3c5e0096a159c4438614 (patch) | |
| tree | 3195260573dc7d0c40b387b1e474609c691ffe62 /net | |
| parent | c62c612446a42a5de3559a1de7623ce86cc51636 (diff) | |
l2tp: fix oops in L2TP IP sockets for connect() AF_UNSPEC case
BugLink: http://bugs.launchpad.net/bugs/1013748
[ Upstream commit c51ce49735c183ef2592db70f918ee698716276b ]
An application may call connect() to disconnect a socket using an
address with family AF_UNSPEC. The L2TP IP sockets were not handling
this case when the socket is not bound and an attempt to connect()
using AF_UNSPEC in such cases would result in an oops. This patch
addresses the problem by protecting the sk_prot->disconnect() call
against trying to unhash the socket before it is bound.
The patch also adds more checks that the sockaddr supplied to bind()
and connect() calls is valid.
RIP: 0010:[<ffffffff82e133b0>] [<ffffffff82e133b0>] inet_unhash+0x50/0xd0
RSP: 0018:ffff88001989be28 EFLAGS: 00010293
Stack:
ffff8800407a8000 0000000000000000 ffff88001989be78 ffffffff82e3a249
ffffffff82e3a050 ffff88001989bec8 ffff88001989be88 ffff8800407a8000
0000000000000010 ffff88001989bec8 ffff88001989bea8 ffffffff82e42639
Call Trace:
[<ffffffff82e3a249>] udp_disconnect+0x1f9/0x290
[<ffffffff82e42639>] inet_dgram_connect+0x29/0x80
[<ffffffff82d012fc>] sys_connect+0x9c/0x100
Reported-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Herton Ronaldo Krzesinski <herton.krzesinski@canonical.com>
Diffstat (limited to 'net')
| -rw-r--r-- | net/l2tp/l2tp_ip.c | 30 |
1 files changed, 24 insertions, 6 deletions
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index ea52d028632..78bc442b2b6 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c | |||
| @@ -251,9 +251,16 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
| 251 | { | 251 | { |
| 252 | struct inet_sock *inet = inet_sk(sk); | 252 | struct inet_sock *inet = inet_sk(sk); |
| 253 | struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *) uaddr; | 253 | struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *) uaddr; |
| 254 | int ret = -EINVAL; | 254 | int ret; |
| 255 | int chk_addr_ret; | 255 | int chk_addr_ret; |
| 256 | 256 | ||
| 257 | if (!sock_flag(sk, SOCK_ZAPPED)) | ||
| 258 | return -EINVAL; | ||
| 259 | if (addr_len < sizeof(struct sockaddr_l2tpip)) | ||
| 260 | return -EINVAL; | ||
| 261 | if (addr->l2tp_family != AF_INET) | ||
| 262 | return -EINVAL; | ||
| 263 | |||
| 257 | ret = -EADDRINUSE; | 264 | ret = -EADDRINUSE; |
| 258 | read_lock_bh(&l2tp_ip_lock); | 265 | read_lock_bh(&l2tp_ip_lock); |
| 259 | if (__l2tp_ip_bind_lookup(&init_net, addr->l2tp_addr.s_addr, sk->sk_bound_dev_if, addr->l2tp_conn_id)) | 266 | if (__l2tp_ip_bind_lookup(&init_net, addr->l2tp_addr.s_addr, sk->sk_bound_dev_if, addr->l2tp_conn_id)) |
| @@ -283,6 +290,8 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
| 283 | sk_del_node_init(sk); | 290 | sk_del_node_init(sk); |
| 284 | write_unlock_bh(&l2tp_ip_lock); | 291 | write_unlock_bh(&l2tp_ip_lock); |
| 285 | ret = 0; | 292 | ret = 0; |
| 293 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
| 294 | |||
| 286 | out: | 295 | out: |
| 287 | release_sock(sk); | 296 | release_sock(sk); |
| 288 | 297 | ||
| @@ -303,13 +312,14 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len | |||
| 303 | __be32 saddr; | 312 | __be32 saddr; |
| 304 | int oif, rc; | 313 | int oif, rc; |
| 305 | 314 | ||
| 306 | rc = -EINVAL; | 315 | if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */ |
| 316 | return -EINVAL; | ||
| 317 | |||
| 307 | if (addr_len < sizeof(*lsa)) | 318 | if (addr_len < sizeof(*lsa)) |
| 308 | goto out; | 319 | return -EINVAL; |
| 309 | 320 | ||
| 310 | rc = -EAFNOSUPPORT; | ||
| 311 | if (lsa->l2tp_family != AF_INET) | 321 | if (lsa->l2tp_family != AF_INET) |
| 312 | goto out; | 322 | return -EAFNOSUPPORT; |
| 313 | 323 | ||
| 314 | lock_sock(sk); | 324 | lock_sock(sk); |
| 315 | 325 | ||
| @@ -363,6 +373,14 @@ out: | |||
| 363 | return rc; | 373 | return rc; |
| 364 | } | 374 | } |
| 365 | 375 | ||
| 376 | static int l2tp_ip_disconnect(struct sock *sk, int flags) | ||
| 377 | { | ||
| 378 | if (sock_flag(sk, SOCK_ZAPPED)) | ||
| 379 | return 0; | ||
| 380 | |||
| 381 | return udp_disconnect(sk, flags); | ||
| 382 | } | ||
| 383 | |||
| 366 | static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr, | 384 | static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr, |
| 367 | int *uaddr_len, int peer) | 385 | int *uaddr_len, int peer) |
| 368 | { | 386 | { |
| @@ -591,7 +609,7 @@ static struct proto l2tp_ip_prot = { | |||
| 591 | .close = l2tp_ip_close, | 609 | .close = l2tp_ip_close, |
| 592 | .bind = l2tp_ip_bind, | 610 | .bind = l2tp_ip_bind, |
| 593 | .connect = l2tp_ip_connect, | 611 | .connect = l2tp_ip_connect, |
| 594 | .disconnect = udp_disconnect, | 612 | .disconnect = l2tp_ip_disconnect, |
| 595 | .ioctl = udp_ioctl, | 613 | .ioctl = udp_ioctl, |
| 596 | .destroy = l2tp_ip_destroy_sock, | 614 | .destroy = l2tp_ip_destroy_sock, |
| 597 | .setsockopt = ip_setsockopt, | 615 | .setsockopt = ip_setsockopt, |
