| From 5d3919a953c3c96c02fc7a337f8376cde43ae31f Mon Sep 17 00:00:00 2001 |
| From: Jakub Sitnicki <jakub@cloudflare.com> |
| Date: Thu, 6 Feb 2020 12:16:52 +0100 |
| Subject: selftests/bpf: Test freeing sockmap/sockhash with a socket in it |
| |
| From: Jakub Sitnicki <jakub@cloudflare.com> |
| |
| commit 5d3919a953c3c96c02fc7a337f8376cde43ae31f upstream. |
| |
| Commit 7e81a3530206 ("bpf: Sockmap, ensure sock lock held during tear |
| down") introduced sleeping issues inside RCU critical sections and while |
| holding a spinlock on sockmap/sockhash tear-down. There has to be at least |
| one socket in the map for the problem to surface. |
| |
| This adds a test that triggers the warnings for broken locking rules. Not a |
| fix per se, but rather tooling to verify the accompanying fixes. Run on a |
| VM with 1 vCPU to reproduce the warnings. |
| |
| Fixes: 7e81a3530206 ("bpf: Sockmap, ensure sock lock held during tear down") |
| Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com> |
| Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> |
| Acked-by: John Fastabend <john.fastabend@gmail.com> |
| Link: https://lore.kernel.org/bpf/20200206111652.694507-4-jakub@cloudflare.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 74 +++++++++++++++++ |
| 1 file changed, 74 insertions(+) |
| |
| --- /dev/null |
| +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c |
| @@ -0,0 +1,74 @@ |
| +// SPDX-License-Identifier: GPL-2.0 |
| +// Copyright (c) 2020 Cloudflare |
| + |
| +#include "test_progs.h" |
| + |
| +static int connected_socket_v4(void) |
| +{ |
| + struct sockaddr_in addr = { |
| + .sin_family = AF_INET, |
| + .sin_port = htons(80), |
| + .sin_addr = { inet_addr("127.0.0.1") }, |
| + }; |
| + socklen_t len = sizeof(addr); |
| + int s, repair, err; |
| + |
| + s = socket(AF_INET, SOCK_STREAM, 0); |
| + if (CHECK_FAIL(s == -1)) |
| + goto error; |
| + |
| + repair = TCP_REPAIR_ON; |
| + err = setsockopt(s, SOL_TCP, TCP_REPAIR, &repair, sizeof(repair)); |
| + if (CHECK_FAIL(err)) |
| + goto error; |
| + |
| + err = connect(s, (struct sockaddr *)&addr, len); |
| + if (CHECK_FAIL(err)) |
| + goto error; |
| + |
| + repair = TCP_REPAIR_OFF_NO_WP; |
| + err = setsockopt(s, SOL_TCP, TCP_REPAIR, &repair, sizeof(repair)); |
| + if (CHECK_FAIL(err)) |
| + goto error; |
| + |
| + return s; |
| +error: |
| + perror(__func__); |
| + close(s); |
| + return -1; |
| +} |
| + |
| +/* Create a map, populate it with one socket, and free the map. */ |
| +static void test_sockmap_create_update_free(enum bpf_map_type map_type) |
| +{ |
| + const int zero = 0; |
| + int s, map, err; |
| + |
| + s = connected_socket_v4(); |
| + if (CHECK_FAIL(s == -1)) |
| + return; |
| + |
| + map = bpf_create_map(map_type, sizeof(int), sizeof(int), 1, 0); |
| + if (CHECK_FAIL(map == -1)) { |
| + perror("bpf_create_map"); |
| + goto out; |
| + } |
| + |
| + err = bpf_map_update_elem(map, &zero, &s, BPF_NOEXIST); |
| + if (CHECK_FAIL(err)) { |
| + perror("bpf_map_update"); |
| + goto out; |
| + } |
| + |
| +out: |
| + close(map); |
| + close(s); |
| +} |
| + |
| +void test_sockmap_basic(void) |
| +{ |
| + if (test__start_subtest("sockmap create_update_free")) |
| + test_sockmap_create_update_free(BPF_MAP_TYPE_SOCKMAP); |
| + if (test__start_subtest("sockhash create_update_free")) |
| + test_sockmap_create_update_free(BPF_MAP_TYPE_SOCKHASH); |
| +} |