Blob Blame History Raw
From 36cf5177c724540aea5a42f9dc6ef5476f86179a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 5 Nov 2021 16:06:45 +0100
Subject: [PATCH] segtree: Fix segfault when restoring a huge interval set

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1908127
Upstream Status: nftables commit baecd1cf26851

commit baecd1cf26851a4c5b7d469206a488f14fe5b147
Author: Phil Sutter <phil@nwl.cc>
Date:   Wed Jun 9 15:49:52 2021 +0200

    segtree: Fix segfault when restoring a huge interval set

    Restoring a set of IPv4 prefixes with about 1.1M elements crashes nft as
    set_to_segtree() exhausts the stack. Prevent this by allocating the
    pointer array on heap and make sure it is freed before returning to
    caller.

    With this patch in place, restoring said set succeeds with allocation of
    about 3GB of memory, according to valgrind.

    Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 src/segtree.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/segtree.c b/src/segtree.c
index d6e3ce2..b852961 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -414,10 +414,10 @@ static int set_to_segtree(struct list_head *msgs, struct set *set,
 			  struct expr *init, struct seg_tree *tree,
 			  bool add, bool merge)
 {
-	struct elementary_interval *intervals[init->size];
+	struct elementary_interval **intervals;
 	struct expr *i, *next;
 	unsigned int n;
-	int err;
+	int err = 0;
 
 	/* We are updating an existing set with new elements, check if the new
 	 * interval overlaps with any of the existing ones.
@@ -428,6 +428,7 @@ static int set_to_segtree(struct list_head *msgs, struct set *set,
 			return err;
 	}
 
+	intervals = xmalloc_array(init->size, sizeof(intervals[0]));
 	n = expr_to_intervals(init, tree->keylen, intervals);
 
 	list_for_each_entry_safe(i, next, &init->expressions, list) {
@@ -446,10 +447,11 @@ static int set_to_segtree(struct list_head *msgs, struct set *set,
 	for (n = 0; n < init->size; n++) {
 		err = ei_insert(msgs, tree, intervals[n], merge);
 		if (err < 0)
-			return err;
+			break;
 	}
 
-	return 0;
+	xfree(intervals);
+	return err;
 }
 
 static bool segtree_needs_first_segment(const struct set *set,
-- 
2.31.1