From 5dc474d6b3ba19df7d491d4eabd9fb7a0c1c2423 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 12 May 2008 15:43:46 -0700 Subject: pppol2tp: Remove null pointer dereference. If session is NULL, it is not possible to access its name field. So I have split apart the printing of the error message to drop the printing of the name field in this case. The macro PRINTK actually only evaluates its arguments starting with the third one if the bitwise conjunction of the first two is non-zero. Normally, this conjunction would only be non-zero if debugging mode were turned on, but when session is NULL, the first argument in both the old and new code is -1, and thus the bitwise conjunction is true. Perhaps a different strategy is desired, such as using tunnel->debug, which session->debug is initialized to, but tunnel can also be NULL, so this does not completely solve the problem. This problem was found using the following semantic match (http://www.emn.fr/x-info/coccinelle/) // @@ expression E, E1; identifier f; statement S1,S2,S3; @@ * if (E == NULL) { ... when != if (E == NULL) S1 else S2 when != E = E1 * E->f ... when any return ...; } else S3 // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/pppol2tp.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/net/pppol2tp.c') diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 244d7830c92a..79359919335b 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -1621,9 +1621,16 @@ out_no_ppp: end: release_sock(sk); - if (error != 0) - PRINTK(session ? session->debug : -1, PPPOL2TP_MSG_CONTROL, KERN_WARNING, - "%s: connect failed: %d\n", session->name, error); + if (error != 0) { + if (session) + PRINTK(session->debug, + PPPOL2TP_MSG_CONTROL, KERN_WARNING, + "%s: connect failed: %d\n", + session->name, error); + else + PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_WARNING, + "connect failed: %d\n", error); + } return error; } -- cgit v1.2.3 From 090c48d3dd5ea90b37350334aaed9a93b0c1e0a1 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Mon, 19 May 2008 14:10:01 -0700 Subject: l2tp: avoid skb truesize bug if headroom is increased A user reported seeing occasional bugs such as the following when using the L2TP driver. SKB BUG: Invalid truesize (272) len=72, sizeof(sk_buff)=208 When L2TP adds its header in the transmit path, it might need to increase the headroom of the skb. In some cases, the increased headroom trips a kernel bug when the skb is freed because the skb has grown beyond its truesize value. The fix is to increase the truesize by the amount of headroom added, after orphaning the skb. While here, fix a misleading comment. Thanks to Iouri Kharon for the initial report and testing the fix. Signed-off-by: James Chapman Signed-off-by: David S. Miller --- drivers/net/pppol2tp.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/net/pppol2tp.c') diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 79359919335b..8db342f2fdc9 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -980,6 +980,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) __wsum csum = 0; struct udphdr *uh; unsigned int len; + int old_headroom; + int new_headroom; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) goto abort; @@ -1001,16 +1003,18 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) /* Check that there's enough headroom in the skb to insert IP, * UDP and L2TP and PPP headers. If not enough, expand it to - * make room. Note that a new skb (or a clone) is - * allocated. If we return an error from this point on, make - * sure we free the new skb but do not free the original skb - * since that is done by the caller for the error case. + * make room. Adjust truesize. */ headroom = NET_SKB_PAD + sizeof(struct iphdr) + sizeof(struct udphdr) + hdr_len + sizeof(ppph); + old_headroom = skb_headroom(skb); if (skb_cow_head(skb, headroom)) goto abort; + new_headroom = skb_headroom(skb); + skb_orphan(skb); + skb->truesize += new_headroom - old_headroom; + /* Setup PPP header */ __skb_push(skb, sizeof(ppph)); skb->data[0] = ppph[0]; @@ -1065,7 +1069,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) /* Get routing info from the tunnel socket */ dst_release(skb->dst); skb->dst = dst_clone(__sk_dst_get(sk_tun)); - skb_orphan(skb); skb->sk = sk_tun; /* Queue the packet to IP for output */ -- cgit v1.2.3 From 199f7d24ae59894243687a234a909f44a8724506 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Wed, 4 Jun 2008 15:07:32 -0700 Subject: lt2p: Fix possible WARN_ON from socket code when UDP socket is closed If an L2TP daemon closes a tunnel socket while packets are queued in the tunnel's reorder queue, a kernel warning is logged because the socket is closed while skbs are still referencing it. The fix is to purge the queue in the socket's release handler. WARNING: at include/net/sock.h:351 udp_lib_unhash+0x41/0x68() Pid: 12998, comm: openl2tpd Not tainted 2.6.25 #8 [] warn_on_slowpath+0x41/0x51 [] udp_lib_unhash+0x41/0x68 [] sk_common_release+0x23/0x90 [] udp_lib_close+0x8/0xa [] inet_release+0x42/0x48 [] sock_release+0x14/0x60 [] sock_close+0x29/0x30 [] __fput+0xad/0x15b [] fput+0x17/0x19 [] filp_close+0x50/0x5a [] sys_close+0x69/0x9f [] syscall_call+0x7/0xb Signed-off-by: James Chapman Signed-off-by: David S. Miller --- drivers/net/pppol2tp.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/net/pppol2tp.c') diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 8db342f2fdc9..04c7e5b407fd 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -1279,6 +1279,7 @@ out: static int pppol2tp_release(struct socket *sock) { struct sock *sk = sock->sk; + struct pppol2tp_session *session; int error; if (!sk) @@ -1296,9 +1297,18 @@ static int pppol2tp_release(struct socket *sock) sock_orphan(sk); sock->sk = NULL; + session = pppol2tp_sock_to_session(sk); + /* Purge any queued data */ skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); + if (session != NULL) { + struct sk_buff *skb; + while ((skb = skb_dequeue(&session->reorder_q))) { + kfree_skb(skb); + sock_put(sk); + } + } release_sock(sk); -- cgit v1.2.3 From 24b95685ffcdb3dc28f64b9e8af6ea3e8360fbc5 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Wed, 4 Jun 2008 15:54:07 -0700 Subject: l2tp: Fix possible oops if transmitting or receiving when tunnel goes down Some problems have been experienced in the field which cause an oops in the pppol2tp driver if L2TP tunnels fail while passing data. The pppol2tp driver uses private data that is referenced via the sk->sk_user_data of its UDP and PPPoL2TP sockets. This patch makes sure that the driver uses sock_hold() when it holds a reference to the sk pointer. This affects its sendmsg(), recvmsg(), getname(), [gs]etsockopt() and ioctl() handlers. Tested by ISP where problem was seen. System has been up 10 days with no oops since running this patch. Without the patch, an oops would occur every 1-2 days. Signed-off-by: James Chapman Signed-off-by: David S. Miller --- drivers/net/pppol2tp.c | 101 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 23 deletions(-) (limited to 'drivers/net/pppol2tp.c') diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 04c7e5b407fd..70cfdb46aa27 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -240,12 +240,15 @@ static inline struct pppol2tp_session *pppol2tp_sock_to_session(struct sock *sk) if (sk == NULL) return NULL; + sock_hold(sk); session = (struct pppol2tp_session *)(sk->sk_user_data); - if (session == NULL) - return NULL; + if (session == NULL) { + sock_put(sk); + goto out; + } BUG_ON(session->magic != L2TP_SESSION_MAGIC); - +out: return session; } @@ -256,12 +259,15 @@ static inline struct pppol2tp_tunnel *pppol2tp_sock_to_tunnel(struct sock *sk) if (sk == NULL) return NULL; + sock_hold(sk); tunnel = (struct pppol2tp_tunnel *)(sk->sk_user_data); - if (tunnel == NULL) - return NULL; + if (tunnel == NULL) { + sock_put(sk); + goto out; + } BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); - +out: return tunnel; } @@ -716,12 +722,14 @@ discard: session->stats.rx_errors++; kfree_skb(skb); sock_put(session->sock); + sock_put(sock); return 0; error: /* Put UDP header back */ __skb_push(skb, sizeof(struct udphdr)); + sock_put(sock); no_tunnel: return 1; @@ -745,10 +753,13 @@ static int pppol2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) "%s: received %d bytes\n", tunnel->name, skb->len); if (pppol2tp_recv_core(sk, skb)) - goto pass_up; + goto pass_up_put; + sock_put(sk); return 0; +pass_up_put: + sock_put(sk); pass_up: return 1; } @@ -858,7 +869,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); if (tunnel == NULL) - goto error; + goto error_put_sess; /* What header length is configured for this session? */ hdr_len = pppol2tp_l2tp_header_len(session); @@ -870,7 +881,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh sizeof(ppph) + total_len, 0, GFP_KERNEL); if (!skb) - goto error; + goto error_put_sess_tun; /* Reserve space for headers. */ skb_reserve(skb, NET_SKB_PAD); @@ -900,7 +911,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh error = memcpy_fromiovec(skb->data, m->msg_iov, total_len); if (error < 0) { kfree_skb(skb); - goto error; + goto error_put_sess_tun; } skb_put(skb, total_len); @@ -947,10 +958,33 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh session->stats.tx_errors++; } + return error; + +error_put_sess_tun: + sock_put(session->tunnel_sock); +error_put_sess: + sock_put(sk); error: return error; } +/* Automatically called when the skb is freed. + */ +static void pppol2tp_sock_wfree(struct sk_buff *skb) +{ + sock_put(skb->sk); +} + +/* For data skbs that we transmit, we associate with the tunnel socket + * but don't do accounting. + */ +static inline void pppol2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk) +{ + sock_hold(sk); + skb->sk = sk; + skb->destructor = pppol2tp_sock_wfree; +} + /* Transmit function called by generic PPP driver. Sends PPP frame * over PPPoL2TP socket. * @@ -993,10 +1027,10 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) sk_tun = session->tunnel_sock; if (sk_tun == NULL) - goto abort; + goto abort_put_sess; tunnel = pppol2tp_sock_to_tunnel(sk_tun); if (tunnel == NULL) - goto abort; + goto abort_put_sess; /* What header length is configured for this session? */ hdr_len = pppol2tp_l2tp_header_len(session); @@ -1009,7 +1043,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) sizeof(struct udphdr) + hdr_len + sizeof(ppph); old_headroom = skb_headroom(skb); if (skb_cow_head(skb, headroom)) - goto abort; + goto abort_put_sess_tun; new_headroom = skb_headroom(skb); skb_orphan(skb); @@ -1069,7 +1103,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) /* Get routing info from the tunnel socket */ dst_release(skb->dst); skb->dst = dst_clone(__sk_dst_get(sk_tun)); - skb->sk = sk_tun; + pppol2tp_skb_set_owner_w(skb, sk_tun); /* Queue the packet to IP for output */ len = skb->len; @@ -1086,8 +1120,14 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) session->stats.tx_errors++; } + sock_put(sk_tun); + sock_put(sk); return 1; +abort_put_sess_tun: + sock_put(sk_tun); +abort_put_sess: + sock_put(sk); abort: /* Free the original skb */ kfree_skb(skb); @@ -1191,7 +1231,7 @@ static void pppol2tp_tunnel_destruct(struct sock *sk) { struct pppol2tp_tunnel *tunnel; - tunnel = pppol2tp_sock_to_tunnel(sk); + tunnel = sk->sk_user_data; if (tunnel == NULL) goto end; @@ -1230,10 +1270,12 @@ static void pppol2tp_session_destruct(struct sock *sk) if (sk->sk_user_data != NULL) { struct pppol2tp_tunnel *tunnel; - session = pppol2tp_sock_to_session(sk); + session = sk->sk_user_data; if (session == NULL) goto out; + BUG_ON(session->magic != L2TP_SESSION_MAGIC); + /* Don't use pppol2tp_sock_to_tunnel() here to * get the tunnel context because the tunnel * socket might have already been closed (its @@ -1611,7 +1653,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, error = ppp_register_channel(&po->chan); if (error) - goto end; + goto end_put_tun; /* This is how we get the session context from the socket. */ sk->sk_user_data = session; @@ -1631,6 +1673,8 @@ out_no_ppp: PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, "%s: created\n", session->name); +end_put_tun: + sock_put(tunnel_sock); end: release_sock(sk); @@ -1678,6 +1722,7 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, *usockaddr_len = len; error = 0; + sock_put(sock->sk); end: return error; @@ -1916,14 +1961,17 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd, err = -EBADF; tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); if (tunnel == NULL) - goto end; + goto end_put_sess; err = pppol2tp_tunnel_ioctl(tunnel, cmd, arg); - goto end; + sock_put(session->tunnel_sock); + goto end_put_sess; } err = pppol2tp_session_ioctl(session, cmd, arg); +end_put_sess: + sock_put(sk); end: return err; } @@ -2069,14 +2117,17 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, err = -EBADF; tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); if (tunnel == NULL) - goto end; + goto end_put_sess; err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val); + sock_put(session->tunnel_sock); } else err = pppol2tp_session_setsockopt(sk, session, optname, val); err = 0; +end_put_sess: + sock_put(sk); end: return err; } @@ -2191,20 +2242,24 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, err = -EBADF; tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); if (tunnel == NULL) - goto end; + goto end_put_sess; err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val); + sock_put(session->tunnel_sock); } else err = pppol2tp_session_getsockopt(sk, session, optname, &val); err = -EFAULT; if (put_user(len, (int __user *) optlen)) - goto end; + goto end_put_sess; if (copy_to_user((void __user *) optval, &val, len)) - goto end; + goto end_put_sess; err = 0; + +end_put_sess: + sock_put(sk); end: return err; } -- cgit v1.2.3 From 6b6707a50c7598a83820077393f8823ab791abf8 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Tue, 10 Jun 2008 12:35:00 -0700 Subject: l2tp: Fix potential memory corruption in pppol2tp_recvmsg() This patch fixes a potential memory corruption in pppol2tp_recvmsg(). If skb->len is bigger than the caller's buffer length, memcpy_toiovec() will go into unintialized data on the kernel heap, interpret it as an iovec and start modifying memory. The fix is to change the memcpy_toiovec() call to skb_copy_datagram_iovec() so that paged packets (rare for PPPOL2TP) are handled properly. Also check that the caller's buffer is big enough for the data and set the MSG_TRUNC flag if it is not so. Reported-by: Ilja Signed-off-by: James Chapman Signed-off-by: David S. Miller --- drivers/net/pppol2tp.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers/net/pppol2tp.c') diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 70cfdb46aa27..f9298827a76c 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -783,14 +783,18 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, err = 0; skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); - if (skb) { - err = memcpy_toiovec(msg->msg_iov, (unsigned char *) skb->data, - skb->len); - if (err < 0) - goto do_skb_free; - err = skb->len; - } -do_skb_free: + if (!skb) + goto end; + + if (len > skb->len) + len = skb->len; + else if (len < skb->len) + msg->msg_flags |= MSG_TRUNC; + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); + if (likely(err == 0)) + err = len; + kfree_skb(skb); end: return err; -- cgit v1.2.3