netlink: hold nlk->cb_mutex longer in __netlink_dump_start()
[ Upstream commit b5590270068c4324dac4a2b5a4a156e02e21339f ] __netlink_dump_start() releases nlk->cb_mutex right before calling netlink_dump() which grabs it again. This seems dangerous, even if KASAN did not bother yet. Add a @lock_taken parameter to netlink_dump() to let it grab the mutex if called from netlink_recvmsg() only. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Jiri Pirko <jiri@nvidia.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
3e56edb0eb
commit
1f62c25f24
@ -130,7 +130,7 @@ static const char *const nlk_cb_mutex_key_strings[MAX_LINKS + 1] = {
|
|||||||
"nlk_cb_mutex-MAX_LINKS"
|
"nlk_cb_mutex-MAX_LINKS"
|
||||||
};
|
};
|
||||||
|
|
||||||
static int netlink_dump(struct sock *sk);
|
static int netlink_dump(struct sock *sk, bool lock_taken);
|
||||||
|
|
||||||
/* nl_table locking explained:
|
/* nl_table locking explained:
|
||||||
* Lookup and traversal are protected with an RCU read-side lock. Insertion
|
* Lookup and traversal are protected with an RCU read-side lock. Insertion
|
||||||
@ -1953,7 +1953,7 @@ static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
|||||||
|
|
||||||
if (READ_ONCE(nlk->cb_running) &&
|
if (READ_ONCE(nlk->cb_running) &&
|
||||||
atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) {
|
atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) {
|
||||||
ret = netlink_dump(sk);
|
ret = netlink_dump(sk, false);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
WRITE_ONCE(sk->sk_err, -ret);
|
WRITE_ONCE(sk->sk_err, -ret);
|
||||||
sk_error_report(sk);
|
sk_error_report(sk);
|
||||||
@ -2163,7 +2163,7 @@ static int netlink_dump_done(struct netlink_sock *nlk, struct sk_buff *skb,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int netlink_dump(struct sock *sk)
|
static int netlink_dump(struct sock *sk, bool lock_taken)
|
||||||
{
|
{
|
||||||
struct netlink_sock *nlk = nlk_sk(sk);
|
struct netlink_sock *nlk = nlk_sk(sk);
|
||||||
struct netlink_ext_ack extack = {};
|
struct netlink_ext_ack extack = {};
|
||||||
@ -2175,6 +2175,7 @@ static int netlink_dump(struct sock *sk)
|
|||||||
int alloc_min_size;
|
int alloc_min_size;
|
||||||
int alloc_size;
|
int alloc_size;
|
||||||
|
|
||||||
|
if (!lock_taken)
|
||||||
mutex_lock(nlk->cb_mutex);
|
mutex_lock(nlk->cb_mutex);
|
||||||
if (!nlk->cb_running) {
|
if (!nlk->cb_running) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
@ -2330,9 +2331,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
|
|||||||
WRITE_ONCE(nlk->cb_running, true);
|
WRITE_ONCE(nlk->cb_running, true);
|
||||||
nlk->dump_done_errno = INT_MAX;
|
nlk->dump_done_errno = INT_MAX;
|
||||||
|
|
||||||
mutex_unlock(nlk->cb_mutex);
|
ret = netlink_dump(sk, true);
|
||||||
|
|
||||||
ret = netlink_dump(sk);
|
|
||||||
|
|
||||||
sock_put(sk);
|
sock_put(sk);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user