Re: [PATCH] can: j1939: j1939_sk_bind(): fix j1939_ecu leak when re-bind failed

[email protected]
Newsgroups org.kernel.vger.linux-can
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Netdevice refcount leak due to a race condition where the unregister notifier misses a socket during bind or re-bind list manipulations.
--

--- Patch [1]: [PATCH] can: j1939: j1939_sk_bind(): fix j1939_ecu leak when re-bind failed ---
commit 472bfe7c306ed38083d747b95430b318ca86c9cf
Author: Tetsuo Handa <[email protected]>

can: j1939: j1939_sk_bind(): fix j1939_ecu leak when re-bind failed

This commit fixes an ECU and net_device reference leak in j1939_sk_bind().
It defers removing the old ECU references during a re-bind operation until
after the new resources have been successfully acquired.

Closes: https://syzkaller.appspot.com/bug?extid=e2af46126e0644cbebdd

> diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
> index 50a598ef5fd4a..24efb25c58c3f 100644
> --- a/net/can/j1939/socket.c
> +++ b/net/can/j1939/socket.c

[ ... ]

> @@ -519,22 +517,31 @@ static int j1939_sk_bind(struct socket *sock, struct sockaddr_unsized *uaddr, in
>  		jsk->priv = priv;
>  	}
>  
> +	/* get new references without dropping old references */
> +	ret = j1939_local_ecu_get(priv, addr->can_addr.j1939.name, addr->can_addr.j1939.addr);
> +	if (ret) {
> +		/* nothing to undo if re-bind() failed */
> +		if (!was_bound) {
> +			j1939_netdev_stop(priv);
> +			jsk->priv = NULL;
> +			synchronize_rcu();
> +			j1939_priv_put(priv);
> +		}
> +		goto out_release_sock;
> +	}
> +
> +	/* drop old references after re-bind() succeeded */
> +	if (was_bound) {
> +		j1939_jsk_del(priv, jsk);

[Severity: High]
This isn't a bug introduced by this patch, but could this create a race
condition with netdevice unregistration during bind and re-bind?

When a socket takes a reference to priv during an initial bind (when
jsk->priv = priv is set), or during a re-bind when it is temporarily
removed from the list via j1939_jsk_del() right here, the socket is holding a
netdevice reference but is absent from the priv->j1939_socks list.

If j1939_sk_netdev_event_unregister() executes concurrently during this
window, it searches for sockets to clean up by iterating that exact list:

net/can/j1939/socket.c:j1939_sk_netdev_event_unregister() {
    ...
    list_for_each_entry(jsk, &priv->j1939_socks, list) {
        ...
    }
}

Could the unregister notifier miss the socket completely while it is
temporarily unlinked, skipping the mandatory cleanup and resulting in
unregister_netdevice() hanging indefinitely in netdev_wait_allrefs()?

> +		j1939_local_ecu_put(priv, jsk->addr.src_name, jsk->addr.sa);
> +	}
> +
>  	/* set default transmit pgn */
>  	if (j1939_pgn_is_valid(addr->can_addr.j1939.pgn))
>  		jsk->pgn_rx_filter = addr->can_addr.j1939.pgn;
>  	jsk->addr.src_name = addr->can_addr.j1939.name;
>  	jsk->addr.sa = addr->can_addr.j1939.addr;
>  
> -	/* get new references */
> -	ret = j1939_local_ecu_get(priv, jsk->addr.src_name, jsk->addr.sa);
> -	if (ret) {
> -		j1939_netdev_stop(priv);
> -		jsk->priv = NULL;
> -		synchronize_rcu();
> -		j1939_priv_put(priv);
> -		goto out_release_sock;
> -	}
> -
>  	j1939_jsk_add(priv, jsk);
>  
>   out_release_sock: /* fall through */

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.