Blame SOURCES/0017-nft-cache-Fix-iptables-save-segfault-under-stress.patch

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