netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] net: tcp: null pointer crash in __inet_put_port
@ 2015-01-28 15:56 Harout Hedeshian
  2015-01-28 21:03 ` Eric Dumazet
  2016-02-17 10:01 ` Zhen
  0 siblings, 2 replies; 3+ messages in thread
From: Harout Hedeshian @ 2015-01-28 15:56 UTC (permalink / raw)
  To: netdev; +Cc: Harout Hedeshian

Hello,

We are observing a crash in __inet_put_port() wherein icsk_bind_hash
is null (stack trace below).

net/ipv4/inet_hashtables.c:
tb = inet_csk(sk)->icsk_bind_hash;
__sk_del_bind_node(sk);
tb->num_owners--;       <-- tb is unconditionally dereferenced here

>From the RAM dumps: [D:0xFFFFFFC0222FB6F8] icsk_bind_hash = 0x0 = ,

The caller of inet_put_port() in this case is tcp_set_state() which
already does a null check on this particular field before calling
inet_put_port().

net/ipv4/tcp.c:
if (inet_csk(sk)->icsk_bind_hash &&
   !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
	inet_put_port(sk);

It seems that inet_put_port() does some locking by disabling bottom
halves, but this locking is happening after the null check.

Additionally, I see tcp_v4_destroy_sock() also calling inet_put_port()
after doing a similar null check. However, I am unsure if it would
ever be called on the same socket.

Does the proposed solution of locking the bottom halves from
tcp_set_state() make any sense here? Or use some other locking mechanism
directly on the socket?

[66356.767342] Unable to handle kernel NULL pointer dereference at virtual address 00000010
[66356.767366] pgd = ffffffc0409cb000
[66356.767377] [00000010] *pgd=00000000409c9003, *pmd=0000000000000000
[66356.767397] Internal error: Oops: 96000006 [#1] PREEMPT SMP
[66356.767409] Modules linked in: ecryptfs(O) texfat(PO) mcKernelApi mcDrvModule moc_crypto_api_tmpl(O) moc_crypto(PO) moc_platform_mod(O)
[66356.767453] CPU: 0 PID: 3735 Comm: GCMReader Tainted: P        W  O 3.10.49-g0d75018 #1
[66356.767467] task: ffffffc03a790ac0 ti: ffffffc02b1d0000 task.ti: ffffffc02b1d0000
[66356.767486] PC is at inet_put_port+0x7c/0xb4
[66356.767498] LR is at inet_put_port+0x64/0xb4
[66356.767509] pc : [<ffffffc000ba3890>] lr : [<ffffffc000ba3878>] pstate: 80000145
[66356.767520] sp : ffffffc02b1d3d00
[66356.767530] x29: ffffffc02b1d3d00 x28: ffffffc02b1d0000
[66356.767544] x27: ffffffc0016cf000 x26: 0000000000000039
[66356.767557] x25: ffffffc055644030 x24: ffffffc026280f10
[66356.767570] x23: ffffffc055644030 x22: ffffffc0222fb398
[66356.767583] x21: ffffffc001b2f4c0 x20: ffffffc0222fb300
[66356.767597] x19: ffffffc0b50b7780 x18: 0000007f8399b000
[66356.767610] x17: 0000007f869e00f8 x16: ffffffc0002fb850
[66356.767624] x15: 0000007f83a4c000 x14: 0000000000000000
[66356.767637] x13: 0000000000430000 x12: 0000000000550000
[66356.767651] x11: 0000000000430000 x10: 0000000000000000
[66356.767663] x9 : b36a52a36930d612 x8 : 0008e12c614dea00
[66356.767676] x7 : 0000000000000018 x6 : 0000000034155555
[66356.767689] x5 : 0000000000000000 x4 : 0000000000000000
[66356.767702] x3 : 0000000000000200 x2 : ffffffc002c17de8
[66356.767715] x1 : 0000000000000000 x0 : 0000000000000000
[66356.767727]
[66356.767739] Process GCMReader (pid: 3735, stack limit = 0xffffffc02b1d0058)
[66356.767749] Call trace:
[66356.767762] [<ffffffc000ba3890>] inet_put_port+0x7c/0xb4
[66356.767777] [<ffffffc000ba6940>] tcp_set_state+0xbc/0x108
[66356.767790] [<ffffffc000baa78c>] tcp_close+0x374/0x420
[66356.767807] [<ffffffc000bca210>] inet_release+0x84/0x98
[66356.767821] [<ffffffc000bfd0d8>] inet6_release+0x34/0x44
[66356.767838] [<ffffffc000b22b38>] sock_release+0x34/0xac
[66356.767852] [<ffffffc000b22bbc>] sock_close+0xc/0x1c
[66356.767868] [<ffffffc0002fd718>] __fput+0x108/0x208
[66356.767882] [<ffffffc0002fd8c0>] ____fput+0x8/0x14
[66356.767898] [<ffffffc00023b8d4>] task_work_run+0xb0/0xe4
[66356.767914] [<ffffffc0002072ec>] do_notify_resume+0x44/0x54
[66356.767929] Code: f941fe81 f9000040 b4000040 f9000402 (b9401020)
[66356.767942] ---[ end trace 421040d8ca7bbe73 ]---
---
 net/ipv4/tcp.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 3075723..f3b5714 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1910,9 +1910,11 @@ void tcp_set_state(struct sock *sk, int state)
 			TCP_INC_STATS(sock_net(sk), TCP_MIB_ESTABRESETS);
 
 		sk->sk_prot->unhash(sk);
+		local_bh_disable();
 		if (inet_csk(sk)->icsk_bind_hash &&
 		    !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
 			inet_put_port(sk);
+		local_bh_enable();
 		/* fall through */
 	default:
 		if (oldstate == TCP_ESTABLISHED)
-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [RFC] net: tcp: null pointer crash in __inet_put_port
  2015-01-28 15:56 [RFC] net: tcp: null pointer crash in __inet_put_port Harout Hedeshian
@ 2015-01-28 21:03 ` Eric Dumazet
  2016-02-17 10:01 ` Zhen
  1 sibling, 0 replies; 3+ messages in thread
From: Eric Dumazet @ 2015-01-28 21:03 UTC (permalink / raw)
  To: Harout Hedeshian; +Cc: netdev

On Wed, 2015-01-28 at 08:56 -0700, Harout Hedeshian wrote:
> Hello,
> 
> We are observing a crash in __inet_put_port() wherein icsk_bind_hash
> is null (stack trace below).
> 
> net/ipv4/inet_hashtables.c:
> tb = inet_csk(sk)->icsk_bind_hash;
> __sk_del_bind_node(sk);
> tb->num_owners--;       <-- tb is unconditionally dereferenced here
> 
> From the RAM dumps: [D:0xFFFFFFC0222FB6F8] icsk_bind_hash = 0x0 = ,
> 
> The caller of inet_put_port() in this case is tcp_set_state() which
> already does a null check on this particular field before calling
> inet_put_port().
> 
> net/ipv4/tcp.c:
> if (inet_csk(sk)->icsk_bind_hash &&
>    !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
> 	inet_put_port(sk);
> 
> It seems that inet_put_port() does some locking by disabling bottom
> halves, but this locking is happening after the null check.
> 
> Additionally, I see tcp_v4_destroy_sock() also calling inet_put_port()
> after doing a similar null check. However, I am unsure if it would
> ever be called on the same socket.
> 
> Does the proposed solution of locking the bottom halves from
> tcp_set_state() make any sense here? Or use some other locking mechanism
> directly on the socket?

>  net/ipv4/tcp.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> index 3075723..f3b5714 100644
> --- a/net/ipv4/tcp.c
> +++ b/net/ipv4/tcp.c
> @@ -1910,9 +1910,11 @@ void tcp_set_state(struct sock *sk, int state)
>  			TCP_INC_STATS(sock_net(sk), TCP_MIB_ESTABRESETS);
>  
>  		sk->sk_prot->unhash(sk);
> +		local_bh_disable();
>  		if (inet_csk(sk)->icsk_bind_hash &&
>  		    !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
>  			inet_put_port(sk);
> +		local_bh_enable();
>  		/* fall through */
>  	default:
>  		if (oldstate == TCP_ESTABLISHED)


I don't think it makes sense.

tcp_set_state() should only be used on a locked socket.

I believe you are papering over another bug.

bh are disabled in inet_put_port because of the spin_lock() that can be
taken both from process and softirq contexts.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC] net: tcp: null pointer crash in __inet_put_port
  2015-01-28 15:56 [RFC] net: tcp: null pointer crash in __inet_put_port Harout Hedeshian
  2015-01-28 21:03 ` Eric Dumazet
@ 2016-02-17 10:01 ` Zhen
  1 sibling, 0 replies; 3+ messages in thread
From: Zhen @ 2016-02-17 10:01 UTC (permalink / raw)
  To: netdev

Hi Harout

I met the same issue.

Is the issue has been fixed by your patch?

Zhen

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2016-02-17 11:35 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-01-28 15:56 [RFC] net: tcp: null pointer crash in __inet_put_port Harout Hedeshian
2015-01-28 21:03 ` Eric Dumazet
2016-02-17 10:01 ` Zhen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).