Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'fixes for concurrent htab updates'

Hou Tao says:

====================

From: Hou Tao <houtao1@huawei.com>

Hi,

The patchset aims to fix the issues found during investigating the
syzkaller problem reported in [0]. It seems that the concurrent updates
to the same hash-table bucket may fail as shown in patch 1.

Patch 1 uses preempt_disable() to fix the problem for
htab_use_raw_lock() case. For !htab_use_raw_lock() case, the problem is
left to "BPF specific memory allocator" patchset [1] in which
!htab_use_raw_lock() will be removed.

Patch 2 fixes the out-of-bound memory read problem reported in [0]. The
problem has the root cause as patch 1 and it is fixed by handling -EBUSY
from htab_lock_bucket() correctly.

Patch 3 add two cases for hash-table update: one for the reentrancy of
bpf_map_update_elem(), and another one for concurrent updates of the
same hash-table bucket.

Comments are always welcome.

Regards,
Tao

[0]: https://lore.kernel.org/bpf/CACkBjsbuxaR6cv0kXJoVnBfL9ZJXjjoUcMpw_Ogc313jSrg14A@mail.gmail.com/
[1]: https://lore.kernel.org/bpf/20220819214232.18784-1-alexei.starovoitov@gmail.com/

Change Log:

v4:
* rebased on bpf-next
* add htab_update to DENYLIST.s390x

v3: https://lore.kernel.org/bpf/20220829023709.1958204-1-houtao@huaweicloud.com/
* patch 1: update commit message and add Fixes tag
* patch 2: add Fixes tag
* patch 3: elaborate the description of test cases

v2: https://lore.kernel.org/bpf/bd60ef93-1c6a-2db2-557d-b09b92ad22bd@huaweicloud.com/
* Note the fix is for CONFIG_PREEMPT case in commit message and add
Reviewed-by tag for patch 1
* Drop patch "bpf: Allow normally concurrent map updates for !htab_use_raw_lock() case"

v1: https://lore.kernel.org/bpf/20220821033223.2598791-1-houtao@huaweicloud.com/
====================

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>

+179 -7
+23 -7
kernel/bpf/hashtab.c
··· 162 162 unsigned long *pflags) 163 163 { 164 164 unsigned long flags; 165 + bool use_raw_lock; 165 166 166 167 hash = hash & HASHTAB_MAP_LOCK_MASK; 167 168 168 - migrate_disable(); 169 + use_raw_lock = htab_use_raw_lock(htab); 170 + if (use_raw_lock) 171 + preempt_disable(); 172 + else 173 + migrate_disable(); 169 174 if (unlikely(__this_cpu_inc_return(*(htab->map_locked[hash])) != 1)) { 170 175 __this_cpu_dec(*(htab->map_locked[hash])); 171 - migrate_enable(); 176 + if (use_raw_lock) 177 + preempt_enable(); 178 + else 179 + migrate_enable(); 172 180 return -EBUSY; 173 181 } 174 182 175 - if (htab_use_raw_lock(htab)) 183 + if (use_raw_lock) 176 184 raw_spin_lock_irqsave(&b->raw_lock, flags); 177 185 else 178 186 spin_lock_irqsave(&b->lock, flags); ··· 193 185 struct bucket *b, u32 hash, 194 186 unsigned long flags) 195 187 { 188 + bool use_raw_lock = htab_use_raw_lock(htab); 189 + 196 190 hash = hash & HASHTAB_MAP_LOCK_MASK; 197 - if (htab_use_raw_lock(htab)) 191 + if (use_raw_lock) 198 192 raw_spin_unlock_irqrestore(&b->raw_lock, flags); 199 193 else 200 194 spin_unlock_irqrestore(&b->lock, flags); 201 195 __this_cpu_dec(*(htab->map_locked[hash])); 202 - migrate_enable(); 196 + if (use_raw_lock) 197 + preempt_enable(); 198 + else 199 + migrate_enable(); 203 200 } 204 201 205 202 static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node); ··· 1704 1691 /* do not grab the lock unless need it (bucket_cnt > 0). */ 1705 1692 if (locked) { 1706 1693 ret = htab_lock_bucket(htab, b, batch, &flags); 1707 - if (ret) 1708 - goto next_batch; 1694 + if (ret) { 1695 + rcu_read_unlock(); 1696 + bpf_enable_instrumentation(); 1697 + goto after_loop; 1698 + } 1709 1699 } 1710 1700 1711 1701 bucket_cnt = 0;
+1
tools/testing/selftests/bpf/DENYLIST.s390x
··· 68 68 setget_sockopt # attach unexpected error: -524 (trampoline) 69 69 cb_refs # expected error message unexpected error: -524 (trampoline) 70 70 cgroup_hierarchical_stats # JIT does not support calling kernel function (kfunc) 71 + htab_update # failed to attach: ERROR: strerror_r(-524)=22 (trampoline)
+126
tools/testing/selftests/bpf/prog_tests/htab_update.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2022. Huawei Technologies Co., Ltd */ 3 + #define _GNU_SOURCE 4 + #include <sched.h> 5 + #include <stdbool.h> 6 + #include <test_progs.h> 7 + #include "htab_update.skel.h" 8 + 9 + struct htab_update_ctx { 10 + int fd; 11 + int loop; 12 + bool stop; 13 + }; 14 + 15 + static void test_reenter_update(void) 16 + { 17 + struct htab_update *skel; 18 + unsigned int key, value; 19 + int err; 20 + 21 + skel = htab_update__open(); 22 + if (!ASSERT_OK_PTR(skel, "htab_update__open")) 23 + return; 24 + 25 + /* lookup_elem_raw() may be inlined and find_kernel_btf_id() will return -ESRCH */ 26 + bpf_program__set_autoload(skel->progs.lookup_elem_raw, true); 27 + err = htab_update__load(skel); 28 + if (!ASSERT_TRUE(!err || err == -ESRCH, "htab_update__load") || err) 29 + goto out; 30 + 31 + skel->bss->pid = getpid(); 32 + err = htab_update__attach(skel); 33 + if (!ASSERT_OK(err, "htab_update__attach")) 34 + goto out; 35 + 36 + /* Will trigger the reentrancy of bpf_map_update_elem() */ 37 + key = 0; 38 + value = 0; 39 + err = bpf_map_update_elem(bpf_map__fd(skel->maps.htab), &key, &value, 0); 40 + if (!ASSERT_OK(err, "add element")) 41 + goto out; 42 + 43 + ASSERT_EQ(skel->bss->update_err, -EBUSY, "no reentrancy"); 44 + out: 45 + htab_update__destroy(skel); 46 + } 47 + 48 + static void *htab_update_thread(void *arg) 49 + { 50 + struct htab_update_ctx *ctx = arg; 51 + cpu_set_t cpus; 52 + int i; 53 + 54 + /* Pinned on CPU 0 */ 55 + CPU_ZERO(&cpus); 56 + CPU_SET(0, &cpus); 57 + pthread_setaffinity_np(pthread_self(), sizeof(cpus), &cpus); 58 + 59 + i = 0; 60 + while (i++ < ctx->loop && !ctx->stop) { 61 + unsigned int key = 0, value = 0; 62 + int err; 63 + 64 + err = bpf_map_update_elem(ctx->fd, &key, &value, 0); 65 + if (err) { 66 + ctx->stop = true; 67 + return (void *)(long)err; 68 + } 69 + } 70 + 71 + return NULL; 72 + } 73 + 74 + static void test_concurrent_update(void) 75 + { 76 + struct htab_update_ctx ctx; 77 + struct htab_update *skel; 78 + unsigned int i, nr; 79 + pthread_t *tids; 80 + int err; 81 + 82 + skel = htab_update__open_and_load(); 83 + if (!ASSERT_OK_PTR(skel, "htab_update__open_and_load")) 84 + return; 85 + 86 + ctx.fd = bpf_map__fd(skel->maps.htab); 87 + ctx.loop = 1000; 88 + ctx.stop = false; 89 + 90 + nr = 4; 91 + tids = calloc(nr, sizeof(*tids)); 92 + if (!ASSERT_NEQ(tids, NULL, "no mem")) 93 + goto out; 94 + 95 + for (i = 0; i < nr; i++) { 96 + err = pthread_create(&tids[i], NULL, htab_update_thread, &ctx); 97 + if (!ASSERT_OK(err, "pthread_create")) { 98 + unsigned int j; 99 + 100 + ctx.stop = true; 101 + for (j = 0; j < i; j++) 102 + pthread_join(tids[j], NULL); 103 + goto out; 104 + } 105 + } 106 + 107 + for (i = 0; i < nr; i++) { 108 + void *thread_err = NULL; 109 + 110 + pthread_join(tids[i], &thread_err); 111 + ASSERT_EQ(thread_err, NULL, "update error"); 112 + } 113 + 114 + out: 115 + if (tids) 116 + free(tids); 117 + htab_update__destroy(skel); 118 + } 119 + 120 + void test_htab_update(void) 121 + { 122 + if (test__start_subtest("reenter_update")) 123 + test_reenter_update(); 124 + if (test__start_subtest("concurrent_update")) 125 + test_concurrent_update(); 126 + }
+29
tools/testing/selftests/bpf/progs/htab_update.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2022. Huawei Technologies Co., Ltd */ 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + #include <bpf/bpf_tracing.h> 6 + 7 + char _license[] SEC("license") = "GPL"; 8 + 9 + struct { 10 + __uint(type, BPF_MAP_TYPE_HASH); 11 + __uint(max_entries, 1); 12 + __uint(key_size, sizeof(__u32)); 13 + __uint(value_size, sizeof(__u32)); 14 + } htab SEC(".maps"); 15 + 16 + int pid = 0; 17 + int update_err = 0; 18 + 19 + SEC("?fentry/lookup_elem_raw") 20 + int lookup_elem_raw(void *ctx) 21 + { 22 + __u32 key = 0, value = 1; 23 + 24 + if ((bpf_get_current_pid_tgid() >> 32) != pid) 25 + return 0; 26 + 27 + update_err = bpf_map_update_elem(&htab, &key, &value, 0); 28 + return 0; 29 + }