git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2] mem-pool: fix big allocations

From
René Scharfe <l.s.r@web.de>
Date
Dec 28, 2023, 19:19 UTC
Message-ID
<1c39c0e7-05b2-4726-a90c-f78df4356a41@web.de>
In-Reply-To
<fa89d269-1a23-4ed6-bebc-30c0b629f444@web.de>

Memory pool allocations that require a new block and would fill at least half of it are handled specially. Before 158dfeff3d (mem-pool: add life cycle management functions, 2018-07-02) they used to be allocated outside of the pool. This patch made mem_pool_alloc() create a bespoke block instead, to allow releasing it when the pool gets discarded.

Unfortunately mem_pool_alloc() returns a pointer to the start of such a bespoke block, i.e. to the struct mp_block at its top. When the caller writes to it, the management information gets corrupted. This affects mem_pool_discard() and -- if there are no other blocks in the pool -- also mem_pool_alloc().

Return the payload pointer of bespoke blocks, just like for smaller allocations, to protect the management struct.

Also update next_free to mark the block as full. This is only strictly necessary for the first allocated block, because subsequent ones are inserted after the current block and never considered for further allocations, but it's easier to just do it in all cases.

Add a basic unit test to demonstrate the issue by using mem_pool_calloc() with a tiny block size, which forces the creation of a bespoke block.

Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: René Scharfe <l.s.r@web.de>
---
Changes since v1:
- simply use check() instead of a custom check_ptr() macro
- drop unnecessary comparison of next_free and end pointers
Interdiff against v1:
  diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
  index 2295779b0b..a0d57df761 100644
  --- a/t/unit-tests/t-mem-pool.c
  +++ b/t/unit-tests/t-mem-pool.c
  @@ -1,8 +1,6 @@
   #include "test-lib.h"
   #include "mem-pool.h"
  -#define check_ptr(a, op, b) check_int(((a) op (b)), ==, 1)
  -
   static void setup_static(void (*f)(struct mem_pool *), size_t block_alloc)
   {
   	struct mem_pool pool = { .block_alloc = block_alloc };
  @@ -16,11 +14,10 @@ static void t_calloc_100(struct mem_pool *pool)
   	char *buffer = mem_pool_calloc(pool, 1, size);
   	for (size_t i = 0; i < size; i++)
   		check_int(buffer[i], ==, 0);
  -	if (!check_ptr(pool->mp_block, !=, NULL))
  +	if (!check(pool->mp_block != NULL))
   		return;
  -	check_ptr(pool->mp_block->next_free, <=, pool->mp_block->end);
  -	check_ptr(pool->mp_block->next_free, !=, NULL);
  -	check_ptr(pool->mp_block->end, !=, NULL);
  +	check(pool->mp_block->next_free != NULL);
  +	check(pool->mp_block->end != NULL);
   }
   int cmd_main(int argc, const char **argv)
 Makefile                  |  1 +
 mem-pool.c                |  6 +++---
 t/unit-tests/t-mem-pool.c | 31 +++++++++++++++++++++++++++++++
 3 files changed, 35 insertions(+), 3 deletions(-)
 create mode 100644 t/unit-tests/t-mem-pool.c
diff --git a/Makefile b/Makefile
index 88ba7a3c51..15990ff312 100644
--- a/Makefile
+++ b/Makefile
@@ -1340,6 +1340,7 @@ THIRD_PARTY_SOURCES += sha1collisiondetection/%
 THIRD_PARTY_SOURCES += sha1dc/%

 UNIT_TEST_PROGRAMS += t-basic
+UNIT_TEST_PROGRAMS += t-mem-pool
 UNIT_TEST_PROGRAMS += t-strbuf
 UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
 UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
diff --git a/mem-pool.c b/mem-pool.c
index c34846d176..e8d976c3ee 100644
--- a/mem-pool.c
+++ b/mem-pool.c
@@ -99,9 +99,9 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)

 	if (!p) {
 		if (len >= (pool->block_alloc / 2))
-			return mem_pool_alloc_block(pool, len, pool->mp_block);
-
-		p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
+			p = mem_pool_alloc_block(pool, len, pool->mp_block);
+		else
+			p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
 	}

 	r = p->next_free;
diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
new file mode 100644
index 0000000000..a0d57df761
--- /dev/null
+++ b/t/unit-tests/t-mem-pool.c
@@ -0,0 +1,31 @@
+#include "test-lib.h"
+#include "mem-pool.h"
+
+static void setup_static(void (*f)(struct mem_pool *), size_t block_alloc)
+{
+	struct mem_pool pool = { .block_alloc = block_alloc };
+	f(&pool);
+	mem_pool_discard(&pool, 0);
+}
+
+static void t_calloc_100(struct mem_pool *pool)
+{
+	size_t size = 100;
+	char *buffer = mem_pool_calloc(pool, 1, size);
+	for (size_t i = 0; i < size; i++)
+		check_int(buffer[i], ==, 0);
+	if (!check(pool->mp_block != NULL))
+		return;
+	check(pool->mp_block->next_free != NULL);
+	check(pool->mp_block->end != NULL);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+	TEST(setup_static(t_calloc_100, 1024 * 1024),
+	     "mem_pool_calloc returns 100 zeroed bytes with big block");
+	TEST(setup_static(t_calloc_100, 1),
+	     "mem_pool_calloc returns 100 zeroed bytes with tiny block");
+
+	return test_done();
+}
--
2.43.0
Previous: René ScharfeNext: phillip.wood123@gmail.com
Message 10 of 12 in “mem-pool: fix big allocations”
  1. mem-pool: fix big allocationsRené Scharfe, Dec 21, 2023
  2. Elijah NewrenDec 24, 2023
  3. René ScharfeDec 24, 2023
  4. Phillip WoodDec 28, 2023
  5. René ScharfeDec 28, 2023
  6. phillip.wood123@gmail.comDec 28, 2023
  7. René ScharfeDec 28, 2023
  8. phillip.wood123@gmail.comDec 28, 2023
  9. René ScharfeDec 29, 2023
  10. mem-pool: fix big allocationsRené Scharfe, Dec 28, 2023
  11. phillip.wood123@gmail.comDec 28, 2023
  12. Taylor BlauJan 2, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.