|
| 1 | +netfilter: nft_set_pipapo: do not free live element |
| 2 | + |
| 3 | +jira LE-2169 |
| 4 | +cve CVE-2024-26924 |
| 5 | +Rebuild_History Non-Buildable kernel-4.18.0-553.27.1.el8_10 |
| 6 | +commit-author Florian Westphal < [email protected]> |
| 7 | +commit 3cfc9ec039af60dbd8965ae085b2c2ccdcfbe1cc |
| 8 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 9 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 10 | +ciq/ciq_backports/kernel-4.18.0-553.27.1.el8_10/3cfc9ec0.failed |
| 11 | + |
| 12 | +Pablo reports a crash with large batches of elements with a |
| 13 | +back-to-back add/remove pattern. Quoting Pablo: |
| 14 | + |
| 15 | + add_elem("00000000") timeout 100 ms |
| 16 | + ... |
| 17 | + add_elem("0000000X") timeout 100 ms |
| 18 | + del_elem("0000000X") <---------------- delete one that was just added |
| 19 | + ... |
| 20 | + add_elem("00005000") timeout 100 ms |
| 21 | + |
| 22 | + 1) nft_pipapo_remove() removes element 0000000X |
| 23 | + Then, KASAN shows a splat. |
| 24 | + |
| 25 | +Looking at the remove function there is a chance that we will drop a |
| 26 | +rule that maps to a non-deactivated element. |
| 27 | + |
| 28 | +Removal happens in two steps, first we do a lookup for key k and return the |
| 29 | +to-be-removed element and mark it as inactive in the next generation. |
| 30 | +Then, in a second step, the element gets removed from the set/map. |
| 31 | + |
| 32 | +The _remove function does not work correctly if we have more than one |
| 33 | +element that share the same key. |
| 34 | + |
| 35 | +This can happen if we insert an element into a set when the set already |
| 36 | +holds an element with same key, but the element mapping to the existing |
| 37 | +key has timed out or is not active in the next generation. |
| 38 | + |
| 39 | +In such case its possible that removal will unmap the wrong element. |
| 40 | +If this happens, we will leak the non-deactivated element, it becomes |
| 41 | +unreachable. |
| 42 | + |
| 43 | +The element that got deactivated (and will be freed later) will |
| 44 | +remain reachable in the set data structure, this can result in |
| 45 | +a crash when such an element is retrieved during lookup (stale |
| 46 | +pointer). |
| 47 | + |
| 48 | +Add a check that the fully matching key does in fact map to the element |
| 49 | +that we have marked as inactive in the deactivation step. |
| 50 | +If not, we need to continue searching. |
| 51 | + |
| 52 | +Add a bug/warn trap at the end of the function as well, the remove |
| 53 | +function must not ever be called with an invisible/unreachable/non-existent |
| 54 | +element. |
| 55 | + |
| 56 | +v2: avoid uneeded temporary variable (Stefano) |
| 57 | + |
| 58 | +Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges") |
| 59 | + Reported-by: Pablo Neira Ayuso < [email protected]> |
| 60 | + Reviewed-by: Stefano Brivio < [email protected]> |
| 61 | + Signed-off-by: Florian Westphal < [email protected]> |
| 62 | + Signed-off-by: Pablo Neira Ayuso < [email protected]> |
| 63 | +(cherry picked from commit 3cfc9ec039af60dbd8965ae085b2c2ccdcfbe1cc) |
| 64 | + Signed-off-by: Jonathan Maple < [email protected]> |
| 65 | + |
| 66 | +# Conflicts: |
| 67 | +# net/netfilter/nft_set_pipapo.c |
| 68 | +diff --cc net/netfilter/nft_set_pipapo.c |
| 69 | +index 4b6a6667d72b,eeaf05ffba95..000000000000 |
| 70 | +--- a/net/netfilter/nft_set_pipapo.c |
| 71 | ++++ b/net/netfilter/nft_set_pipapo.c |
| 72 | +@@@ -1880,14 -2089,14 +1882,20 @@@ static void nft_pipapo_remove(const str |
| 73 | + rules_fx = f->mt[start].n; |
| 74 | + start = f->mt[start].to; |
| 75 | + |
| 76 | +++<<<<<<< HEAD |
| 77 | + + match_start += NFT_PIPAPO_GROUPS_PADDED_SIZE(f->groups); |
| 78 | + + match_end += NFT_PIPAPO_GROUPS_PADDED_SIZE(f->groups); |
| 79 | + + } |
| 80 | +- |
| 81 | +- if (i == m->field_count) { |
| 82 | +- priv->dirty = true; |
| 83 | +- pipapo_drop(m, rulemap); |
| 84 | +- return; |
| 85 | +++======= |
| 86 | ++ match_start += NFT_PIPAPO_GROUPS_PADDED_SIZE(f); |
| 87 | ++ match_end += NFT_PIPAPO_GROUPS_PADDED_SIZE(f); |
| 88 | +++>>>>>>> 3cfc9ec039af (netfilter: nft_set_pipapo: do not free live element) |
| 89 | ++ |
| 90 | ++ if (last && f->mt[rulemap[i].to].e == e) { |
| 91 | ++ priv->dirty = true; |
| 92 | ++ pipapo_drop(m, rulemap); |
| 93 | ++ return; |
| 94 | ++ } |
| 95 | + } |
| 96 | + |
| 97 | + first_rule += rules_f0; |
| 98 | +* Unmerged path net/netfilter/nft_set_pipapo.c |
0 commit comments