KASAN reports following splat: BUG: KASAN: slab-out-of-bounds in pipapo_get_avx2+0x941/0x25d0 Read of size 1 at addr ffff88814c561be0 by task nft/3944 CPU: 7 UID: 0 PID: 3944 Comm: nft Not tainted 6.17.0-rc4+ #637 PREEMPT(full) Call Trace: pipapo_get_avx2+0x941/0x25d0 ? __local_bh_enable_ip+0x116/0x1a0 ? pipapo_get_avx2+0xee/0x25d0 ? nft_pipapo_insert+0x22b/0x11b0 nft_pipapo_insert+0x440/0x11b0 nf_tables_newsetelem+0x220a/0x3a00 .. This bisects down to 84c1da7b38d9 ("netfilter: nft_set_pipapo: use AVX2 algorithm for insertions too"), however, it merely uncovers this bug. When we find a match but that match has expired or timed out, the AVX2 implementation restarts the full match loop. At that point, data (key element or start of register space with the key) has already been incremented to point to the last key field: out-of-bounds access occurs. The restart logic in AVX2 is different compared to the plain C implementation, but both should follow the same logic. The C implementation just calls pipapo_refill() again to check the next entry. Do the same in the AVX2 implementation. Note that with this change, due to implementation differences of pipapo_refill vs. nft_pipapo_avx2_refill, the refill call will return the same element again, then, on the next call it will move to the next entry as expected. This is because avx2_refill doesn't clear the bitmap in the 'last' conditional. This is harmless. A selftest test case comes in a followup patch. Sent as RFC tag because it needs to be revamped after net -> net-next merge, there are conflicting changes in these two trees at the moment. Another alternative is to retarget this patch to nf, but given its a day-0 bug that only got exposed due to the use of AVX2 in insertion path added recently I think -next is fine. Cc: Stefano Brivio Fixes: 7400b063969b ("nft_set_pipapo: Introduce AVX2-based lookup implementation") Signed-off-by: Florian Westphal --- net/netfilter/nft_set_pipapo_avx2.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c index 7559306d0aed..d97b67a4de16 100644 --- a/net/netfilter/nft_set_pipapo_avx2.c +++ b/net/netfilter/nft_set_pipapo_avx2.c @@ -1179,7 +1179,6 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, nft_pipapo_avx2_prepare(); -next_match: nft_pipapo_for_each_field(f, i, m) { bool last = i == m->field_count - 1, first = !i; int ret = 0; @@ -1226,6 +1225,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, #undef NFT_SET_PIPAPO_AVX2_LOOKUP +next_match: if (ret < 0) { scratch->map_index = map_index; kernel_fpu_end(); @@ -1238,8 +1238,10 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, e = f->mt[ret].e; if (unlikely(__nft_set_elem_expired(&e->ext, tstamp) || - !nft_set_elem_active(&e->ext, genmask))) - goto next_match; + !nft_set_elem_active(&e->ext, genmask))) { + ret = pipapo_refill(res, f->bsize, f->rules, fill, f->mt, last); + goto next_match: + } scratch->map_index = map_index; kernel_fpu_end(); -- 2.49.1 Add a test case for bug resolved with: 'netfilter: nft_set_pipapo_avx2: fix skip of expired entries'. It passes on nf.git (it uses the generic/C version for insertion duplicate check) but fails on unpatched nf-next if AVX2 is supported: cannot create same element twice 0s [FAIL] Could create element twice in same transaction table inet filter { # handle 8 [..] elements = { 1.2.3.4 . 1.2.4.1 counter packets 0 bytes 0, 1.2.4.1 . 1.2.3.4 counter packets 0 bytes 0, 1.2.3.4 . 1.2.4.1 counter packets 0 bytes 0, 1.2.4.1 . 1.2.3.4 counter packets 0 bytes 0 } Cc: Stefano Brivio Signed-off-by: Florian Westphal --- .../net/netfilter/nft_concat_range.sh | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/netfilter/nft_concat_range.sh b/tools/testing/selftests/net/netfilter/nft_concat_range.sh index 20e76b395c85..4d4d5004684c 100755 --- a/tools/testing/selftests/net/netfilter/nft_concat_range.sh +++ b/tools/testing/selftests/net/netfilter/nft_concat_range.sh @@ -29,7 +29,7 @@ TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto net6_port_net6_port net_port_mac_proto_net" # Reported bugs, also described by TYPE_ variables below -BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch" +BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate" # List of possible paths to pktgen script from kernel tree for performance tests PKTGEN_SCRIPT_PATHS=" @@ -408,6 +408,18 @@ perf_duration 0 " +TYPE_doublecreate=" +display cannot create same element twice +type_spec ipv4_addr . ipv4_addr +chain_spec ip saddr . ip daddr +dst addr4 +proto icmp + +race_repeat 0 + +perf_duration 0 +" + # Set template for all tests, types and rules are filled in depending on test set_template=' flush ruleset @@ -1900,6 +1912,30 @@ test_bug_avx2_mismatch() fi } +test_bug_doublecreate() +{ + local elements="1.2.3.4 . 1.2.4.1, 1.2.4.1 . 1.2.3.4" + local ret=1 + + setup veth send_"${proto}" set || return ${ksft_skip} + + nft add element inet filter test "{ $elements }" +nft -f - </dev/null +flush set inet filter test +create element inet filter test { $elements } +create element inet filter test { $elements } +EOF + ret=$? + + if [ $ret -eq 0 ];then + err "Could create element twice in same transaction" + err "$(nft -a list ruleset)" + return 1 + fi + + return 0 +} + test_reported_issues() { eval test_bug_"${subtest}" } -- 2.49.1