Commit e3607d546d for strongswan.org

commit e3607d546d753a1831dbb532d3148b2cc653f8e6
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Mon Jul 13 15:16:15 2026 +0200

    mem-pool: Enforce size limit also for range-based pools and fix overflow

    Due to the overflow when mapping addresses to offsets, no addresses could
    get released when the pool was defined in a way that the last address
    assignable is 255.255.255.255 (probably never the case in practice).

    While not really useful in practice, the range 0.0.0.0-255.255.255.255
    resulted in an empty pool (`size = 0xffffffff + 1`), which triggers the
    %config like behavior.  Since the implementation currently uses signed
    integers throughout, we make sure the largest assignable offset is
    INT_MAX.

    The two off-by-one fixes (`>= pool->size`) were never an issue in practice
    because `get_new()` has a guard that caps the offset at the size and
    released addresses originally came from the pool so are always in range.

    Fixes: 0897cda33b63 ("Add a constructor to create in-memory pools from an address range")

diff --git a/src/libcharon/attributes/mem_pool.c b/src/libcharon/attributes/mem_pool.c
index a7d0394480..1db5b3f265 100644
--- a/src/libcharon/attributes/mem_pool.c
+++ b/src/libcharon/attributes/mem_pool.c
@@ -161,7 +161,7 @@ static host_t *apply_offset(host_t *base, int offset)
 static host_t* offset2host(private_mem_pool_t *pool, int offset)
 {
 	offset--;
-	if (offset > pool->size)
+	if (offset >= pool->size)
 	{
 		return NULL;
 	}
@@ -194,7 +194,7 @@ static int host2offset(private_mem_pool_t *pool, host_t *addr)
 	}
 	hosti = ntohl(*(uint32_t*)(host.ptr));
 	basei = ntohl(*(uint32_t*)(base.ptr));
-	if (hosti > basei + pool->size)
+	if (hosti - basei >= pool->size)
 	{
 		return -1;
 	}
@@ -742,11 +742,15 @@ mem_pool_t *mem_pool_create_range(char *name, host_t *from, host_t *to)
 		DBG1(DBG_CFG, "IP address range too large: %H-%H", from, to);
 		return NULL;
 	}
-	this = create_generic(name);
-	this->base = from->clone(from);
 	diff = untoh32(toaddr.ptr + toaddr.len - sizeof(diff)) -
 		   untoh32(fromaddr.ptr + fromaddr.len - sizeof(diff));
+	if (diff >= (1U << POOL_LIMIT) - 1)
+	{
+		DBG1(DBG_CFG, "IP address range too large: %H-%H", from, to);
+		return NULL;
+	}
+	this = create_generic(name);
+	this->base = from->clone(from);
 	this->size = diff + 1;
-
 	return &this->public;
 }
diff --git a/src/libcharon/tests/suites/test_mem_pool.c b/src/libcharon/tests/suites/test_mem_pool.c
index ae10e3a36c..35b7a4884f 100644
--- a/src/libcharon/tests/suites/test_mem_pool.c
+++ b/src/libcharon/tests/suites/test_mem_pool.c
@@ -63,6 +63,20 @@ static void assert_acquire(mem_pool_t *pool, char *requested, char *expected,
 	id->destroy(id);
 }

+static void assert_release(mem_pool_t *pool, char *released)
+{
+	identification_t *id;
+	host_t *rel;
+
+	id = identification_create_from_string("tester");
+	rel = host_create_from_string(released, 0);
+
+	ck_assert(pool->release_address(pool, rel, id));
+
+	rel->destroy(rel);
+	id->destroy(id);
+}
+
 static void assert_acquires_new(mem_pool_t *pool, char *pattern, int first)
 {
 	char expected[16];
@@ -222,6 +236,28 @@ START_TEST(test_range)
 	assert_acquires_new(pool, "192.168.0.%d", 10);
 	pool->destroy(pool);

+	from->destroy(from);
+	from = host_create_from_string("255.255.255.254", 0);
+	to->destroy(to);
+	to = host_create_from_string("255.255.255.255", 0);
+	pool = mem_pool_create_range("test", from, to);
+	ck_assert_int_eq(2, pool->get_size(pool));
+	assert_base(pool, "255.255.255.254");
+	assert_acquires_new(pool, "255.255.255.%d", 254);
+	assert_release(pool, "255.255.255.255");
+	ck_assert_int_eq(1, pool->get_online(pool));
+	assert_acquire(pool, "0.0.0.0", "255.255.255.255", MEM_POOL_EXISTING);
+	ck_assert_int_eq(2, pool->get_online(pool));
+	pool->destroy(pool);
+
+	/* this range is too large */
+	from->destroy(from);
+	from = host_create_from_string("0.0.0.0", 0);
+	to->destroy(to);
+	to = host_create_from_string("255.255.255.255", 0);
+	pool = mem_pool_create_range("test", from, to);
+	ck_assert(!pool);
+
 	from->destroy(from);
 	from = host_create_from_string("fec::1", 0);
 	to->destroy(to);
@@ -229,6 +265,14 @@ START_TEST(test_range)
 	pool = mem_pool_create_range("test", from, to);
 	ck_assert(!pool);

+	/* this range is too large */
+	from->destroy(from);
+	from = host_create_from_string("fec::", 0);
+	to->destroy(to);
+	to = host_create_from_string("fec::ffff:ffff", 0);
+	pool = mem_pool_create_range("test", from, to);
+	ck_assert(!pool);
+
 	from->destroy(from);
 	to->destroy(to);
 }