|
|
576484 |
From 38c94a9f5ea03deffe0a34056a0f83a4af4641bb Mon Sep 17 00:00:00 2001
|
|
|
576484 |
From: Phil Sutter <phil@nwl.cc>
|
|
|
576484 |
Date: Fri, 13 Mar 2020 13:02:12 +0100
|
|
|
576484 |
Subject: [PATCH] nft: cache: Fix iptables-save segfault under stress
|
|
|
576484 |
|
|
|
576484 |
If kernel ruleset is constantly changing, code called by
|
|
|
576484 |
nft_is_table_compatible() may crash: For each item in table's chain
|
|
|
576484 |
list, nft_is_chain_compatible() is called. This in turn calls
|
|
|
576484 |
nft_build_cache() to fetch chain's rules. Though if kernel genid has changed
|
|
|
576484 |
meanwhile, cache is flushed and rebuilt from scratch, thereby freeing
|
|
|
576484 |
table's chain list - the foreach loop in nft_is_table_compatible() then
|
|
|
576484 |
operates on freed memory.
|
|
|
576484 |
|
|
|
576484 |
A simple reproducer (may need a few calls):
|
|
|
576484 |
|
|
|
576484 |
| RULESET='*filter
|
|
|
576484 |
| :INPUT ACCEPT [10517:1483527]
|
|
|
576484 |
| :FORWARD ACCEPT [0:0]
|
|
|
576484 |
| :OUTPUT ACCEPT [1714:105671]
|
|
|
576484 |
| COMMIT
|
|
|
576484 |
| '
|
|
|
576484 |
|
|
|
|
576484 |
| for ((i = 0; i < 100; i++)); do
|
|
|
576484 |
| iptables-nft-restore <<< "$RULESET" &
|
|
|
576484 |
| done &
|
|
|
576484 |
| iptables-nft-save
|
|
|
576484 |
|
|
|
576484 |
To fix the problem, basically revert commit ab1cd3b510fa5 ("nft: ensure
|
|
|
576484 |
cache consistency") so that __nft_build_cache() no longer flushes the
|
|
|
576484 |
cache. Instead just record kernel's genid when fetching for the first
|
|
|
576484 |
time. If kernel rule set changes until the changes are committed, the
|
|
|
576484 |
commit simply fails and local cache is being rebuilt.
|
|
|
576484 |
|
|
|
576484 |
Signed-off-by: Phil Sutter <phil@nwl.cc>
|
|
|
576484 |
(cherry picked from commit 200bc399651499f502ac0de45f4d4aa4c9d37ab6)
|
|
|
576484 |
Signed-off-by: Phil Sutter <psutter@redhat.com>
|
|
|
576484 |
---
|
|
|
576484 |
iptables/nft-cache.c | 16 ++--------------
|
|
|
576484 |
1 file changed, 2 insertions(+), 14 deletions(-)
|
|
|
576484 |
|
|
|
576484 |
diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c
|
|
|
576484 |
index 6f21f2283e0fb..07265b7795e4f 100644
|
|
|
576484 |
--- a/iptables/nft-cache.c
|
|
|
576484 |
+++ b/iptables/nft-cache.c
|
|
|
576484 |
@@ -452,15 +452,11 @@ __nft_build_cache(struct nft_handle *h, enum nft_cache_level level,
|
|
|
576484 |
const struct builtin_table *t, const char *set,
|
|
|
576484 |
const char *chain)
|
|
|
576484 |
{
|
|
|
576484 |
- uint32_t genid_start, genid_stop;
|
|
|
576484 |
-
|
|
|
576484 |
if (level <= h->cache_level)
|
|
|
576484 |
return;
|
|
|
576484 |
-retry:
|
|
|
576484 |
- mnl_genid_get(h, &genid_start);
|
|
|
576484 |
|
|
|
576484 |
- if (h->cache_level && genid_start != h->nft_genid)
|
|
|
576484 |
- flush_chain_cache(h, NULL);
|
|
|
576484 |
+ if (!h->nft_genid)
|
|
|
576484 |
+ mnl_genid_get(h, &h->nft_genid);
|
|
|
576484 |
|
|
|
576484 |
switch (h->cache_level) {
|
|
|
576484 |
case NFT_CL_NONE:
|
|
|
576484 |
@@ -487,18 +483,10 @@ retry:
|
|
|
576484 |
break;
|
|
|
576484 |
}
|
|
|
576484 |
|
|
|
576484 |
- mnl_genid_get(h, &genid_stop);
|
|
|
576484 |
- if (genid_start != genid_stop) {
|
|
|
576484 |
- flush_chain_cache(h, NULL);
|
|
|
576484 |
- goto retry;
|
|
|
576484 |
- }
|
|
|
576484 |
-
|
|
|
576484 |
if (!t && !chain)
|
|
|
576484 |
h->cache_level = level;
|
|
|
576484 |
else if (h->cache_level < NFT_CL_TABLES)
|
|
|
576484 |
h->cache_level = NFT_CL_TABLES;
|
|
|
576484 |
-
|
|
|
576484 |
- h->nft_genid = genid_start;
|
|
|
576484 |
}
|
|
|
576484 |
|
|
|
576484 |
void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c)
|
|
|
576484 |
--
|
|
|
576484 |
2.25.1
|
|
|
576484 |
|