Skip to content

Commit

Permalink
l2tp: Serialize access to sk_user_data with sk_callback_lock
Browse files Browse the repository at this point in the history
sk->sk_user_data has multiple users, which are not compatible with each
other. Writers must synchronize by grabbing the sk->sk_callback_lock.

l2tp currently fails to grab the lock when modifying the underlying tunnel
socket fields. Fix it by adding appropriate locking.

We err on the side of safety and grab the sk_callback_lock also inside the
sk_destruct callback overridden by l2tp, even though there should be no
refs allowing access to the sock at the time when sk_destruct gets called.

v4:
- serialize write to sk_user_data in l2tp sk_destruct

v3:
- switch from sock lock to sk_callback_lock
- document write-protection for sk_user_data

v2:
- update Fixes to point to origin of the bug
- use real names in Reported/Tested-by tags

Cc: Tom Parkin <tparkin@katalix.com>
Fixes: 3557baa ("[L2TP]: PPP over L2TP driver core")
Reported-by: Haowei Yan <g1042620637@gmail.com>
Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Jakub Sitnicki authored and David S. Miller committed Nov 16, 2022
1 parent f524b72 commit b68777d
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 7 deletions.
2 changes: 1 addition & 1 deletion include/net/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ struct sk_filter;
* @sk_tskey: counter to disambiguate concurrent tstamp requests
* @sk_zckey: counter to order MSG_ZEROCOPY notifications
* @sk_socket: Identd and reporting IO signals
* @sk_user_data: RPC layer private data
* @sk_user_data: RPC layer private data. Write-protected by @sk_callback_lock.
* @sk_frag: cached page frag
* @sk_peek_off: current peek_offset value
* @sk_send_head: front of stuff to transmit
Expand Down
19 changes: 13 additions & 6 deletions net/l2tp/l2tp_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1150,8 +1150,10 @@ static void l2tp_tunnel_destruct(struct sock *sk)
}

/* Remove hooks into tunnel socket */
write_lock_bh(&sk->sk_callback_lock);
sk->sk_destruct = tunnel->old_sk_destruct;
sk->sk_user_data = NULL;
write_unlock_bh(&sk->sk_callback_lock);

/* Call the original destructor */
if (sk->sk_destruct)
Expand Down Expand Up @@ -1469,16 +1471,18 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
sock = sockfd_lookup(tunnel->fd, &ret);
if (!sock)
goto err;

ret = l2tp_validate_socket(sock->sk, net, tunnel->encap);
if (ret < 0)
goto err_sock;
}

sk = sock->sk;
write_lock(&sk->sk_callback_lock);

ret = l2tp_validate_socket(sk, net, tunnel->encap);
if (ret < 0)
goto err_sock;

tunnel->l2tp_net = net;
pn = l2tp_pernet(net);

sk = sock->sk;
sock_hold(sk);
tunnel->sock = sk;

Expand All @@ -1504,7 +1508,7 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,

setup_udp_tunnel_sock(net, sock, &udp_cfg);
} else {
sk->sk_user_data = tunnel;
rcu_assign_sk_user_data(sk, tunnel);
}

tunnel->old_sk_destruct = sk->sk_destruct;
Expand All @@ -1518,13 +1522,16 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
if (tunnel->fd >= 0)
sockfd_put(sock);

write_unlock(&sk->sk_callback_lock);
return 0;

err_sock:
if (tunnel->fd < 0)
sock_release(sock);
else
sockfd_put(sock);

write_unlock(&sk->sk_callback_lock);
err:
return ret;
}
Expand Down

0 comments on commit b68777d

Please sign in to comment.