Skip to contentSkip to conversation

dccp: Unlock sock before calling sk_free()

PATCH Applied Arnaldo Carvalho de Melo 3 participants 5 messages Mar 1, 2017 – Mar 3, 2017

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/2 dccp: Unlock sock before calling sk_free()

From: Arnaldo Carvalho de Melo <[email protected]>

The code where sk_clone() came from created a new socket and locked it, but then, on the error path didn't unlock it.

This problem stayed there for a long while, till b0691c8ee7c2 ("net:
Unlock sock before calling sk_free()") fixed it, but unfortunately the callers of sk_clone() (now sk_clone_locked()) were not audited and the one in dccp_create_openreq_child() remained.

Now in the age of the syskaller fuzzer, this was finally uncovered, as reported by Dmitry:

---- 8< ----

I've got the following report while running syzkaller fuzzer on 86292b33d4b7 ("Merge branch 'akpm' (patches from Andrew)")

[ BUG: held lock freed! ]
4.10.0+ #234 Not tainted
-------------------------
syz-executor6/6898 is freeing memory ffff88006286cac0-ffff88006286d3b7, with a lock still held there!
(slock-AF_INET6){+.-...}, at: [<ffffffff8362c2c9>] spin_lock
include/linux/spinlock.h:299 [inline]
(slock-AF_INET6){+.-...}, at: [<ffffffff8362c2c9>]

  sk_clone_lock+0x3d9/0x12c0 net/core/sock.c:1504
  5 locks held by syz-executor6/6898:
   #0:  (sk_lock-AF_INET6){+.+.+.}, at: [<ffffffff839a34b4>] lock_sock
  include/net/sock.h:1460 [inline]
   #0:  (sk_lock-AF_INET6){+.+.+.}, at: [<ffffffff839a34b4>]
  inet_stream_connect+0x44/0xa0 net/ipv4/af_inet.c:681
   #1:  (rcu_read_lock){......}, at: [<ffffffff83bc1c2a>]
  inet6_csk_xmit+0x12a/0x5d0 net/ipv6/inet6_connection_sock.c:126
   #2:  (rcu_read_lock){......}, at: [<ffffffff8369b424>] __skb_unlink
  include/linux/skbuff.h:1767 [inline]
   #2:  (rcu_read_lock){......}, at: [<ffffffff8369b424>] __skb_dequeue
  include/linux/skbuff.h:1783 [inline]
   #2:  (rcu_read_lock){......}, at: [<ffffffff8369b424>]
  process_backlog+0x264/0x730 net/core/dev.c:4835
   #3:  (rcu_read_lock){......}, at: [<ffffffff83aeb5c0>]
  ip6_input_finish+0x0/0x1700 net/ipv6/ip6_input.c:59
   #4:  (slock-AF_INET6){+.-...}, at: [<ffffffff8362c2c9>] spin_lock
  include/linux/spinlock.h:299 [inline]
   #4:  (slock-AF_INET6){+.-...}, at: [<ffffffff8362c2c9>]
  sk_clone_lock+0x3d9/0x12c0 net/core/sock.c:1504

Fix it just like was done by b0691c8ee7c2 ("net: Unlock sock before calling sk_free()").

---
 net/dccp/minisocks.c | 1 +
 1 file changed, 1 insertion(+)
net/dccp/minisocks.c +1 -0
index 53eddf99e4f6..d20d948a98ed 100644
@@ -122,6 +122,7 @@ struct sock *dccp_create_openreq_child(const struct sock *sk,
 			/* It is still raw copy of parent, so invalidate
 			 * destructor and make plain sk_free() */
 			newsk->sk_destruct = NULL;
+			bh_unlock_sock(newsk);
 			sk_free(newsk);
 			return NULL;
 		}
Signature · 1 line
2.9.3
#1 lore raw

PATCH 2/2 net: Introduce sk_clone_lock() error path routine

From: Arnaldo Carvalho de Melo <[email protected]>

When handling problems in cloning a socket with the sk_clone_locked() function we need to perform several steps that were open coded in it and its callers, so introduce a routine to avoid this duplication:
sk_free_unlock_clone().

---
 include/net/sock.h   |  1 +
 net/core/sock.c      | 16 +++++++++++-----
 net/dccp/minisocks.c |  6 +-----
 3 files changed, 13 insertions(+), 10 deletions(-)
include/net/sock.h +1 -0
index c4f5e6fca17c..93d1160bcd32 100644
@@ -1520,6 +1520,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
 void sk_free(struct sock *sk);
 void sk_destruct(struct sock *sk);
 struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority);
+void sk_free_unlock_clone(struct sock *sk);
 
 struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force,
 			     gfp_t priority);
net/core/sock.c +11 -5
index 4eca27dc5c94..a3d9bb20f65d 100644
@@ -1540,11 +1540,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
 			is_charged = sk_filter_charge(newsk, filter);
 
 		if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
-			/* It is still raw copy of parent, so invalidate
-			 * destructor and make plain sk_free() */
-			newsk->sk_destruct = NULL;
-			bh_unlock_sock(newsk);
-			sk_free(newsk);
+			sk_free_unlock_clone(newsk);
 			newsk = NULL;
 			goto out;
 		}
@@ -1593,6 +1589,16 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
 }
 EXPORT_SYMBOL_GPL(sk_clone_lock);
 
+void sk_free_unlock_clone(struct sock *sk)
+{
+	/* It is still raw copy of parent, so invalidate
+	 * destructor and make plain sk_free() */
+	sk->sk_destruct = NULL;
+	bh_unlock_sock(sk);
+	sk_free(sk);
+}
+EXPORT_SYMBOL_GPL(sk_free_unlock_clone);
+
 void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
 {
 	u32 max_segs = 1;
net/dccp/minisocks.c +1 -5
index d20d948a98ed..e267e6f4c9a5 100644
@@ -119,11 +119,7 @@ struct sock *dccp_create_openreq_child(const struct sock *sk,
 		 * Activate features: initialise CCIDs, sequence windows etc.
 		 */
 		if (dccp_feat_activate_values(newsk, &dreq->dreq_featneg)) {
-			/* It is still raw copy of parent, so invalidate
-			 * destructor and make plain sk_free() */
-			newsk->sk_destruct = NULL;
-			bh_unlock_sock(newsk);
-			sk_free(newsk);
+			sk_free_unlock_clone(newsk);
 			return NULL;
 		}
 		dccp_init_xmit_timers(newsk);
Signature · 1 line
2.9.3
maintainer Applied
#2 lore raw

Re: [PATCH 2/2] net: Introduce sk_clone_lock() error path routine

From: Arnaldo Carvalho de Melo <[email protected]>
Date: Wed, 1 Mar 2017 16:35:08 -0300

Quoted 7 lines

From: Arnaldo Carvalho de Melo <[email protected]>

When handling problems in cloning a socket with the sk_clone_locked() function we need to perform several steps that were open coded in it and its callers, so introduce a routine to avoid this duplication:
sk_free_unlock_clone().

Quoted 7 lines

Applied.

#3 lore raw

Re: [PATCH 2/2] net: Introduce sk_clone_lock() error path routine

In reply to Arnaldo Carvalho de Melo

Hello!

Quoted 14 lines · Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <[email protected]>

When handling problems in cloning a socket with the sk_clone_locked() function we need to perform several steps that were open coded in it and its callers, so introduce a routine to avoid this duplication:
sk_free_unlock_clone().

[...]

Quoted 18 lines
net/core/sock.c +1 -5
index 4eca27dc5c94..a3d9bb20f65d 100644
@@ -1540,11 +1540,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
 			is_charged = sk_filter_charge(newsk, filter);
 
 		if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
-			/* It is still raw copy of parent, so invalidate
-			 * destructor and make plain sk_free() */
-			newsk->sk_destruct = NULL;
-			bh_unlock_sock(newsk);
-			sk_free(newsk);
+			sk_free_unlock_clone(newsk);
 			newsk = NULL;
 			goto out;
 		}

net/core/sock.c struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) · line 1595

Quoted 7 lines
net/core/sock.c +4 -0
@@ -1593,6 +1589,16 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
 }
 EXPORT_SYMBOL_GPL(sk_clone_lock);
 
+void sk_free_unlock_clone(struct sock *sk)
+{
+	/* It is still raw copy of parent, so invalidate
+	 * destructor and make plain sk_free() */

Could fix the comment style to the preferred one, while at it:

/* bla
* bla
*/

net/core/sock.c struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) · line 1604

Quoted 9 lines
net/core/sock.c +6 -0
+	sk->sk_destruct = NULL;
+	bh_unlock_sock(sk);
+	sk_free(sk);
+}
+EXPORT_SYMBOL_GPL(sk_free_unlock_clone);
+
 void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
 {
 	u32 max_segs = 1;

[...]

MBR, Sergei

maintainer
#4 lore raw
In reply to Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <[email protected]>
Date: Wed, 1 Mar 2017 16:35:07 -0300

Quoted 12 lines

From: Arnaldo Carvalho de Melo <[email protected]>

The code where sk_clone() came from created a new socket and locked it, but then, on the error path didn't unlock it.

This problem stayed there for a long while, till b0691c8ee7c2 ("net:
Unlock sock before calling sk_free()") fixed it, but unfortunately the callers of sk_clone() (now sk_clone_locked()) were not audited and the one in dccp_create_openreq_child() remained.

Now in the age of the syskaller fuzzer, this was finally uncovered, as reported by Dmitry:

...

Quoted 10 lines

Fix it just like was done by b0691c8ee7c2 ("net: Unlock sock before calling sk_free()").

Applied and queued up for -stable.