diff mbox

netlink: fix compat recvmsg

Message ID 1281943244.3683.8.camel@jlt3.sipsolutions.net
State Accepted, archived
Delegated to: David Miller
Headers show

Commit Message

Johannes Berg Aug. 16, 2010, 7:20 a.m. UTC
From: Johannes Berg <johannes.berg@intel.com>

Since
commit 1dacc76d0014a034b8aca14237c127d7c19d7726
Author: Johannes Berg <johannes@sipsolutions.net>
Date:   Wed Jul 1 11:26:02 2009 +0000

    net/compat/wext: send different messages to compat tasks

we had a race condition when setting and then
restoring frag_list. Eric attempted to fix it,
but the fix created even worse problems.

However, the original motivation I had when I
added the code that turned out to be racy is
no longer clear to me, since we only copy up
to skb->len to userspace, which doesn't include
the frag_list length. As a result, not doing
any frag_list clearing and restoring avoids
the race condition, while not introducing any
other problems.

Additionally, while preparing this patch I found
that since none of the remaining netlink code is
really aware of the frag_list, we need to use the
original skb's information for packet information
and credentials. This fixes, for example, the
group information received by compat tasks.

Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: stable@kernel.org [2.6.31+, for 2.6.35 revert 1235f504aa]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/netlink/af_netlink.c |   46 ++++++++++++++++------------------------------
 1 file changed, 16 insertions(+), 30 deletions(-)



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Eric Dumazet Aug. 16, 2010, 12:50 p.m. UTC | #1
Le lundi 16 août 2010 à 09:20 +0200, Johannes Berg a écrit :
> From: Johannes Berg <johannes.berg@intel.com>
> 
> Since
> commit 1dacc76d0014a034b8aca14237c127d7c19d7726
> Author: Johannes Berg <johannes@sipsolutions.net>
> Date:   Wed Jul 1 11:26:02 2009 +0000
> 
>     net/compat/wext: send different messages to compat tasks
> 
> we had a race condition when setting and then
> restoring frag_list. Eric attempted to fix it,
> but the fix created even worse problems.
> 
> However, the original motivation I had when I
> added the code that turned out to be racy is
> no longer clear to me, since we only copy up
> to skb->len to userspace, which doesn't include
> the frag_list length. As a result, not doing
> any frag_list clearing and restoring avoids
> the race condition, while not introducing any
> other problems.
> 
> Additionally, while preparing this patch I found
> that since none of the remaining netlink code is
> really aware of the frag_list, we need to use the
> original skb's information for packet information
> and credentials. This fixes, for example, the
> group information received by compat tasks.
> 
> Cc: Eric Dumazet <eric.dumazet@gmail.com>
> Cc: stable@kernel.org [2.6.31+, for 2.6.35 revert 1235f504aa]
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---

As a side effect, it might fix the 

	if (nlk->flags & NETLINK_RECV_PKTINFO)
		netlink_cmsg_recv_pktinfo(msg, skb);

That tried to use pktinfo from the slave skb. After this
patch, skb points to master skb.

Acked-by: Eric Dumazet <eric.dumazet@gmail.com>

Thanks !


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Aug. 16, 2010, 12:54 p.m. UTC | #2
On Mon, 2010-08-16 at 14:50 +0200, Eric Dumazet wrote:

> > Additionally, while preparing this patch I found
> > that since none of the remaining netlink code is
> > really aware of the frag_list, we need to use the
> > original skb's information for packet information
> > and credentials. This fixes, for example, the
> > group information received by compat tasks.

> As a side effect, it might fix the 
> 
> 	if (nlk->flags & NETLINK_RECV_PKTINFO)
> 		netlink_cmsg_recv_pktinfo(msg, skb);
> 
> That tried to use pktinfo from the slave skb. After this
> patch, skb points to master skb.

Yeah, that's what I was looking at regarding the "packet information" I
wrote about. The group information was wrong (0 rather than 1), I could
see that in strace (though I had to compile a strace64 binary first...)

> Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
> 
> Thanks !

Thanks for looking, and finding the bug to start with :-)

johannes

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Eric Dumazet Aug. 16, 2010, 1:01 p.m. UTC | #3
Le lundi 16 août 2010 à 14:50 +0200, Eric Dumazet a écrit :
> Le lundi 16 août 2010 à 09:20 +0200, Johannes Berg a écrit :
> > From: Johannes Berg <johannes.berg@intel.com>
> > 
> > Since
> > commit 1dacc76d0014a034b8aca14237c127d7c19d7726
> > Author: Johannes Berg <johannes@sipsolutions.net>
> > Date:   Wed Jul 1 11:26:02 2009 +0000
> > 
> >     net/compat/wext: send different messages to compat tasks
> > 
> > we had a race condition when setting and then
> > restoring frag_list. Eric attempted to fix it,
> > but the fix created even worse problems.
> > 
> > However, the original motivation I had when I
> > added the code that turned out to be racy is
> > no longer clear to me, since we only copy up
> > to skb->len to userspace, which doesn't include
> > the frag_list length. As a result, not doing
> > any frag_list clearing and restoring avoids
> > the race condition, while not introducing any
> > other problems.
> > 
> > Additionally, while preparing this patch I found
> > that since none of the remaining netlink code is
> > really aware of the frag_list, we need to use the
> > original skb's information for packet information
> > and credentials. This fixes, for example, the
> > group information received by compat tasks.
> > 
> > Cc: Eric Dumazet <eric.dumazet@gmail.com>
> > Cc: stable@kernel.org [2.6.31+, for 2.6.35 revert 1235f504aa]
> > Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> > ---
> 
> As a side effect, it might fix the 
> 
> 	if (nlk->flags & NETLINK_RECV_PKTINFO)
> 		netlink_cmsg_recv_pktinfo(msg, skb);
> 
> That tried to use pktinfo from the slave skb. After this
> patch, skb points to master skb.
> 

As you already mentioned it in your changelog, I missed it ;)



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller Aug. 19, 2010, 6:36 a.m. UTC | #4
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Mon, 16 Aug 2010 14:50:25 +0200

> Le lundi 16 août 2010 à 09:20 +0200, Johannes Berg a écrit :
>> From: Johannes Berg <johannes.berg@intel.com>
>> 
>> Since
>> commit 1dacc76d0014a034b8aca14237c127d7c19d7726
>> Author: Johannes Berg <johannes@sipsolutions.net>
>> Date:   Wed Jul 1 11:26:02 2009 +0000
>> 
>>     net/compat/wext: send different messages to compat tasks
>> 
>> we had a race condition when setting and then
>> restoring frag_list. Eric attempted to fix it,
>> but the fix created even worse problems.
 ...
>> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
 ...
> Acked-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied, thanks guys!
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

--- wireless-testing.orig/net/netlink/af_netlink.c	2010-08-16 08:33:42.000000000 +0200
+++ wireless-testing/net/netlink/af_netlink.c	2010-08-16 09:13:17.000000000 +0200
@@ -1406,7 +1406,7 @@  static int netlink_recvmsg(struct kiocb
 	struct netlink_sock *nlk = nlk_sk(sk);
 	int noblock = flags&MSG_DONTWAIT;
 	size_t copied;
-	struct sk_buff *skb, *frag __maybe_unused = NULL;
+	struct sk_buff *skb, *data_skb;
 	int err;
 
 	if (flags&MSG_OOB)
@@ -1418,45 +1418,35 @@  static int netlink_recvmsg(struct kiocb
 	if (skb == NULL)
 		goto out;
 
+	data_skb = skb;
+
 #ifdef CONFIG_COMPAT_NETLINK_MESSAGES
 	if (unlikely(skb_shinfo(skb)->frag_list)) {
-		bool need_compat = !!(flags & MSG_CMSG_COMPAT);
-
 		/*
-		 * If this skb has a frag_list, then here that means that
-		 * we will have to use the frag_list skb for compat tasks
-		 * and the regular skb for non-compat tasks.
+		 * If this skb has a frag_list, then here that means that we
+		 * will have to use the frag_list skb's data for compat tasks
+		 * and the regular skb's data for normal (non-compat) tasks.
 		 *
-		 * The skb might (and likely will) be cloned, so we can't
-		 * just reset frag_list and go on with things -- we need to
-		 * keep that. For the compat case that's easy -- simply get
-		 * a reference to the compat skb and free the regular one
-		 * including the frag. For the non-compat case, we need to
-		 * avoid sending the frag to the user -- so assign NULL but
-		 * restore it below before freeing the skb.
+		 * If we need to send the compat skb, assign it to the
+		 * 'data_skb' variable so that it will be used below for data
+		 * copying. We keep 'skb' for everything else, including
+		 * freeing both later.
 		 */
-		if (need_compat) {
-			struct sk_buff *compskb = skb_shinfo(skb)->frag_list;
-			skb_get(compskb);
-			kfree_skb(skb);
-			skb = compskb;
-		} else {
-			frag = skb_shinfo(skb)->frag_list;
-			skb_shinfo(skb)->frag_list = NULL;
-		}
+		if (flags & MSG_CMSG_COMPAT)
+			data_skb = skb_shinfo(skb)->frag_list;
 	}
 #endif
 
 	msg->msg_namelen = 0;
 
-	copied = skb->len;
+	copied = data_skb->len;
 	if (len < copied) {
 		msg->msg_flags |= MSG_TRUNC;
 		copied = len;
 	}
 
-	skb_reset_transport_header(skb);
-	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	skb_reset_transport_header(data_skb);
+	err = skb_copy_datagram_iovec(data_skb, 0, msg->msg_iov, copied);
 
 	if (msg->msg_name) {
 		struct sockaddr_nl *addr = (struct sockaddr_nl *)msg->msg_name;
@@ -1476,11 +1466,7 @@  static int netlink_recvmsg(struct kiocb
 	}
 	siocb->scm->creds = *NETLINK_CREDS(skb);
 	if (flags & MSG_TRUNC)
-		copied = skb->len;
-
-#ifdef CONFIG_COMPAT_NETLINK_MESSAGES
-	skb_shinfo(skb)->frag_list = frag;
-#endif
+		copied = data_skb->len;
 
 	skb_free_datagram(sk, skb);