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

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