Skip to content

Commit

Permalink
bpf: sockmap, sock_map_delete needs to use xchg
Browse files Browse the repository at this point in the history
__sock_map_delete() may be called from a tcp event such as unhash or
close from the following trace,

  tcp_bpf_close()
    tcp_bpf_remove()
      sk_psock_unlink()
        sock_map_delete_from_link()
          __sock_map_delete()

In this case the sock lock is held but this only protects against
duplicate removals on the TCP side. If the map is free'd then we have
this trace,

  sock_map_free
    xchg()                  <- replaces map entry
    sock_map_unref()
      sk_psock_put()
        sock_map_del_link()

The __sock_map_delete() call however uses a read, test, null over the
map entry which can result in both paths trying to free the map
entry.

To fix use xchg in TCP paths as well so we avoid having two references
to the same map entry.

Fixes: 604326b ("bpf, sockmap: convert to generic sk_msg interface")
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
  • Loading branch information
John Fastabend authored and Daniel Borkmann committed Jul 22, 2019
1 parent 32857cf commit 45a4521
Showing 1 changed file with 9 additions and 5 deletions.
14 changes: 9 additions & 5 deletions net/core/sock_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,16 +276,20 @@ static int __sock_map_delete(struct bpf_stab *stab, struct sock *sk_test,
struct sock **psk)
{
struct sock *sk;
int err = 0;

raw_spin_lock_bh(&stab->lock);
sk = *psk;
if (!sk_test || sk_test == sk)
*psk = NULL;
sk = xchg(psk, NULL);

if (likely(sk))
sock_map_unref(sk, psk);
else
err = -EINVAL;

raw_spin_unlock_bh(&stab->lock);
if (unlikely(!sk))
return -EINVAL;
sock_map_unref(sk, psk);
return 0;
return err;
}

static void sock_map_delete_from_link(struct bpf_map *map, struct sock *sk,
Expand Down

0 comments on commit 45a4521

Please sign in to comment.