diff mbox series

[net,v2,1/2] vsock: Orphan socket after transport release

Message ID 20250206-vsock-linger-nullderef-v2-1-f8a1f19146f8@rbox.co (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series vsock: null-ptr-deref when SO_LINGER enabled | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 1 maintainers not CCed: virtualization@lists.linux.dev
netdev/build_clang success Errors and warnings before: 2 this patch: 2
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 30 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2025-02-06--00-00 (tests: 891)

Commit Message

Michal Luczaj Feb. 5, 2025, 11:06 p.m. UTC
During socket release, sock_orphan() is called without considering that it
sets sk->sk_wq to NULL. Later, if SO_LINGER is enabled, this leads to a
null pointer dereferenced in virtio_transport_wait_close().

Orphan the socket only after transport release.

While there, reflow the other comment.

Partially reverts the 'Fixes:' commit.

KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f]
 lock_acquire+0x19e/0x500
 _raw_spin_lock_irqsave+0x47/0x70
 add_wait_queue+0x46/0x230
 virtio_transport_release+0x4e7/0x7f0
 __vsock_release+0xfd/0x490
 vsock_release+0x90/0x120
 __sock_release+0xa3/0x250
 sock_close+0x14/0x20
 __fput+0x35e/0xa90
 __x64_sys_close+0x78/0xd0
 do_syscall_64+0x93/0x1b0
 entry_SYSCALL_64_after_hwframe+0x76/0x7e

Reported-by: syzbot+9d55b199192a4be7d02c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9d55b199192a4be7d02c
Fixes: fcdd2242c023 ("vsock: Keep the binding until socket destruction")
Tested-by: Luigi Leonardi <leonardi@redhat.com>
Signed-off-by: Michal Luczaj <mhal@rbox.co>
---
 net/vmw_vsock/af_vsock.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

Comments

Stefano Garzarella Feb. 10, 2025, 10:15 a.m. UTC | #1
On Thu, Feb 06, 2025 at 12:06:47AM +0100, Michal Luczaj wrote:
>During socket release, sock_orphan() is called without considering that it
>sets sk->sk_wq to NULL. Later, if SO_LINGER is enabled, this leads to a
>null pointer dereferenced in virtio_transport_wait_close().
>
>Orphan the socket only after transport release.
>
>While there, reflow the other comment.
>
>Partially reverts the 'Fixes:' commit.
>
>KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f]
> lock_acquire+0x19e/0x500
> _raw_spin_lock_irqsave+0x47/0x70
> add_wait_queue+0x46/0x230
> virtio_transport_release+0x4e7/0x7f0
> __vsock_release+0xfd/0x490
> vsock_release+0x90/0x120
> __sock_release+0xa3/0x250
> sock_close+0x14/0x20
> __fput+0x35e/0xa90
> __x64_sys_close+0x78/0xd0
> do_syscall_64+0x93/0x1b0
> entry_SYSCALL_64_after_hwframe+0x76/0x7e
>
>Reported-by: syzbot+9d55b199192a4be7d02c@syzkaller.appspotmail.com
>Closes: https://syzkaller.appspot.com/bug?extid=9d55b199192a4be7d02c
>Fixes: fcdd2242c023 ("vsock: Keep the binding until socket destruction")
>Tested-by: Luigi Leonardi <leonardi@redhat.com>
>Signed-off-by: Michal Luczaj <mhal@rbox.co>
>---
> net/vmw_vsock/af_vsock.c | 15 ++++++++++-----
> 1 file changed, 10 insertions(+), 5 deletions(-)
>
>diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
>index 075695173648d3a4ecbd04e908130efdbb393b41..85d20891b771a25b8172a163983054a2557f98c1 100644
>--- a/net/vmw_vsock/af_vsock.c
>+++ b/net/vmw_vsock/af_vsock.c
>@@ -817,20 +817,25 @@ static void __vsock_release(struct sock *sk, int level)
> 	vsk = vsock_sk(sk);
> 	pending = NULL;	/* Compiler warning. */
>
>-	/* When "level" is SINGLE_DEPTH_NESTING, use the nested
>-	 * version to avoid the warning "possible recursive locking
>-	 * detected". When "level" is 0, lock_sock_nested(sk, level)
>-	 * is the same as lock_sock(sk).
>+	/* When "level" is SINGLE_DEPTH_NESTING, use the nested version to avoid
>+	 * the warning "possible recursive locking detected". When "level" is 0,
>+	 * lock_sock_nested(sk, level) is the same as lock_sock(sk).

This comment is formatted “weird” because recently in commit
135ffc7becc8 ("bpf, vsock: Invoke proto::close on close()") we reduced
the indentation without touching the comment.

Since this is a fix we may have to backport into stable branches without
that commit, I would avoid touching it to avoid unnecessary conflicts.

The rest LGTM!

Thanks for the quick fix and sorry for the late reply but FOSDEM-flu
caught me.

Thanks,
Stefano

> 	 */
> 	lock_sock_nested(sk, level);
>
>-	sock_orphan(sk);
>+	/* Indicate to vsock_remove_sock() that the socket is being released and
>+	 * can be removed from the bound_table. Unlike transport reassignment
>+	 * case, where the socket must remain bound despite vsock_remove_sock()
>+	 * being called from the transport release() callback.
>+	 */
>+	sock_set_flag(sk, SOCK_DEAD);
>
> 	if (vsk->transport)
> 		vsk->transport->release(vsk);
> 	else if (sock_type_connectible(sk->sk_type))
> 		vsock_remove_sock(vsk);
>
>+	sock_orphan(sk);
> 	sk->sk_shutdown = SHUTDOWN_MASK;
>
> 	skb_queue_purge(&sk->sk_receive_queue);
>
>-- 
>2.48.1
>
Luigi Leonardi Feb. 10, 2025, 10:24 a.m. UTC | #2
Hi Michal,

On Thu, Feb 06, 2025 at 12:06:47AM +0100, Michal Luczaj wrote:
>During socket release, sock_orphan() is called without considering that it
>sets sk->sk_wq to NULL. Later, if SO_LINGER is enabled, this leads to a
>null pointer dereferenced in virtio_transport_wait_close().
>
>Orphan the socket only after transport release.
>
>While there, reflow the other comment.
May I ask you why?
>
>Partially reverts the 'Fixes:' commit.
>
>KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f]
> lock_acquire+0x19e/0x500
> _raw_spin_lock_irqsave+0x47/0x70
> add_wait_queue+0x46/0x230
> virtio_transport_release+0x4e7/0x7f0
> __vsock_release+0xfd/0x490
> vsock_release+0x90/0x120
> __sock_release+0xa3/0x250
> sock_close+0x14/0x20
> __fput+0x35e/0xa90
> __x64_sys_close+0x78/0xd0
> do_syscall_64+0x93/0x1b0
> entry_SYSCALL_64_after_hwframe+0x76/0x7e
>
>Reported-by: syzbot+9d55b199192a4be7d02c@syzkaller.appspotmail.com
>Closes: https://syzkaller.appspot.com/bug?extid=9d55b199192a4be7d02c
>Fixes: fcdd2242c023 ("vsock: Keep the binding until socket destruction")
>Tested-by: Luigi Leonardi <leonardi@redhat.com>
>Signed-off-by: Michal Luczaj <mhal@rbox.co>
>---
> net/vmw_vsock/af_vsock.c | 15 ++++++++++-----
> 1 file changed, 10 insertions(+), 5 deletions(-)
>
>diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
>index 075695173648d3a4ecbd04e908130efdbb393b41..85d20891b771a25b8172a163983054a2557f98c1 100644
>--- a/net/vmw_vsock/af_vsock.c
>+++ b/net/vmw_vsock/af_vsock.c
>@@ -817,20 +817,25 @@ static void __vsock_release(struct sock *sk, int level)
> 	vsk = vsock_sk(sk);
> 	pending = NULL;	/* Compiler warning. */
>
>-	/* When "level" is SINGLE_DEPTH_NESTING, use the nested
>-	 * version to avoid the warning "possible recursive locking
>-	 * detected". When "level" is 0, lock_sock_nested(sk, level)
>-	 * is the same as lock_sock(sk).
>+	/* When "level" is SINGLE_DEPTH_NESTING, use the nested version to avoid
>+	 * the warning "possible recursive locking detected". When "level" is 0,
>+	 * lock_sock_nested(sk, level) is the same as lock_sock(sk).
> 	 */
> 	lock_sock_nested(sk, level);
>
>-	sock_orphan(sk);
>+	/* Indicate to vsock_remove_sock() that the socket is being released and
>+	 * can be removed from the bound_table. Unlike transport reassignment
>+	 * case, where the socket must remain bound despite vsock_remove_sock()
>+	 * being called from the transport release() callback.
>+	 */
>+	sock_set_flag(sk, SOCK_DEAD);
>
> 	if (vsk->transport)
> 		vsk->transport->release(vsk);
> 	else if (sock_type_connectible(sk->sk_type))
> 		vsock_remove_sock(vsk);
>
>+	sock_orphan(sk);
> 	sk->sk_shutdown = SHUTDOWN_MASK;
>
> 	skb_queue_purge(&sk->sk_receive_queue);
>
>-- 
>2.48.1
>

Code LGTM!

I probably wouldn't have changed that comment because of possible 
conflicts.

Reviewed-by: Luigi Leonardi <leonardi@redhat.com>

Thank you,
Luigi
Michal Luczaj Feb. 10, 2025, 12:43 p.m. UTC | #3
On 2/10/25 11:24, Luigi Leonardi wrote:
> Hi Michal,
> 
> On Thu, Feb 06, 2025 at 12:06:47AM +0100, Michal Luczaj wrote:
>> During socket release, sock_orphan() is called without considering that it
>> sets sk->sk_wq to NULL. Later, if SO_LINGER is enabled, this leads to a
>> null pointer dereferenced in virtio_transport_wait_close().
>>
>> Orphan the socket only after transport release.
>>
>> While there, reflow the other comment.
> May I ask you why?

For aesthetic purposes only :)

> ...
> Code LGTM!
> 
> I probably wouldn't have changed that comment because of possible 
> conflicts.
> 
> Reviewed-by: Luigi Leonardi <leonardi@redhat.com>

Got it, dropping the old comment reflow, thanks.

Michal
Michal Luczaj Feb. 10, 2025, 12:43 p.m. UTC | #4
On 2/10/25 11:15, Stefano Garzarella wrote:
> On Thu, Feb 06, 2025 at 12:06:47AM +0100, Michal Luczaj wrote:
>> ...
>> diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
>> index 075695173648d3a4ecbd04e908130efdbb393b41..85d20891b771a25b8172a163983054a2557f98c1 100644
>> --- a/net/vmw_vsock/af_vsock.c
>> +++ b/net/vmw_vsock/af_vsock.c
>> @@ -817,20 +817,25 @@ static void __vsock_release(struct sock *sk, int level)
>> 	vsk = vsock_sk(sk);
>> 	pending = NULL;	/* Compiler warning. */
>>
>> -	/* When "level" is SINGLE_DEPTH_NESTING, use the nested
>> -	 * version to avoid the warning "possible recursive locking
>> -	 * detected". When "level" is 0, lock_sock_nested(sk, level)
>> -	 * is the same as lock_sock(sk).
>> +	/* When "level" is SINGLE_DEPTH_NESTING, use the nested version to avoid
>> +	 * the warning "possible recursive locking detected". When "level" is 0,
>> +	 * lock_sock_nested(sk, level) is the same as lock_sock(sk).
> 
> This comment is formatted “weird” because recently in commit
> 135ffc7becc8 ("bpf, vsock: Invoke proto::close on close()") we reduced
> the indentation without touching the comment.
> 
> Since this is a fix we may have to backport into stable branches without
> that commit, I would avoid touching it to avoid unnecessary conflicts.
> ...

I've checked that 135ffc7becc8 was already backported (6.1, 6.6, 6.12) and
thought it's safe to do, but I understand your concern, so sending v3
without touching the other comment.

Thanks,
Michal
diff mbox series

Patch

diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 075695173648d3a4ecbd04e908130efdbb393b41..85d20891b771a25b8172a163983054a2557f98c1 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -817,20 +817,25 @@  static void __vsock_release(struct sock *sk, int level)
 	vsk = vsock_sk(sk);
 	pending = NULL;	/* Compiler warning. */
 
-	/* When "level" is SINGLE_DEPTH_NESTING, use the nested
-	 * version to avoid the warning "possible recursive locking
-	 * detected". When "level" is 0, lock_sock_nested(sk, level)
-	 * is the same as lock_sock(sk).
+	/* When "level" is SINGLE_DEPTH_NESTING, use the nested version to avoid
+	 * the warning "possible recursive locking detected". When "level" is 0,
+	 * lock_sock_nested(sk, level) is the same as lock_sock(sk).
 	 */
 	lock_sock_nested(sk, level);
 
-	sock_orphan(sk);
+	/* Indicate to vsock_remove_sock() that the socket is being released and
+	 * can be removed from the bound_table. Unlike transport reassignment
+	 * case, where the socket must remain bound despite vsock_remove_sock()
+	 * being called from the transport release() callback.
+	 */
+	sock_set_flag(sk, SOCK_DEAD);
 
 	if (vsk->transport)
 		vsk->transport->release(vsk);
 	else if (sock_type_connectible(sk->sk_type))
 		vsock_remove_sock(vsk);
 
+	sock_orphan(sk);
 	sk->sk_shutdown = SHUTDOWN_MASK;
 
 	skb_queue_purge(&sk->sk_receive_queue);