[PATCH] Fix talloc memlimits to work correctly with pools.

Jeremy Allison jra at samba.org
Tue Aug 27 16:33:54 MDT 2013


On Tue, Aug 27, 2013 at 01:44:47PM -0700, Jeremy Allison wrote:
> The current talloc_memlimit code is broken
> w.r.t talloc_pools. It accounts for the
> original pool allocation, and then also
> counts all the sub-allocations from the
> pool against the memlimit, which it
> shouldn't (only the original pool
> allocation actually does a malloc).
> 
> This patchset fixes that, and adds
> a simple test case to show memory
> limits working with pools.
> 
> After this local.talloc test is
> valgrind clean.
> 
> Please review and push if you're
> happy with it.
> 
> Once this is in I'll rebase Volker's
> nested talloc pool patch on top of
> it and submit that for review.

Here is the revised patchset
with Simo's reviews applied.

Patch #1 - remove ssize_t code - don't use
casts.

Patch #2 - make commit comment clearer.

Patch #7 - fix logic error I introduced.
We must call _talloc_total_limit_size()
if tc->limit || new_tc->limit).

Patch #10 - remove ssize_t size_change
variable. Keep both old_size and new_size
as size_t instead.

Cheers,

	Jeremy.
-------------- next part --------------
>From 2155bc5af6ffa11c910ccf04012952cb41288e02 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:36:23 -0700
Subject: [PATCH 01/13] Start to fix talloc memlimits with talloc pools.

Add the functions:

talloc_memlimit_grow(), talloc_memlimit_shrink(),
talloc_memlimit_update_on_free().

as replacements for talloc_memlimit_update().
The interface to talloc_memlimit_update() is very
hard to understand and use. The above functions
are (to me) much clearer.

The goal of these changes is to only update
the memlimits on malloc/free/realloc, not
on every pool allocation. That way we only
count pool creation as allocation from any
imposed limits, not allocation from an already
created pool.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 76f0aee..067d46f 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -238,6 +238,11 @@ struct talloc_memlimit {
 static bool talloc_memlimit_check(struct talloc_memlimit *limit, size_t size);
 static bool talloc_memlimit_update(struct talloc_memlimit *limit,
 				   size_t old_size, size_t new_size);
+static void talloc_memlimit_grow(struct talloc_memlimit *limit,
+				size_t size);
+static void talloc_memlimit_shrink(struct talloc_memlimit *limit,
+				size_t size);
+static void talloc_memlimit_update_on_free(struct talloc_chunk *tc);
 
 typedef int (*talloc_destructor_t)(void *);
 
@@ -2564,6 +2569,73 @@ static bool talloc_memlimit_check(struct talloc_memlimit *limit, size_t size)
 	return true;
 }
 
+/*
+  Update memory limits when freeing a talloc_chunk.
+*/
+static void talloc_memlimit_update_on_free(struct talloc_chunk *tc)
+{
+	if (!tc->limit) {
+		return;
+	}
+
+	/*
+	 * Pool entries don't count. Only the pools
+	 * themselves are counted as part of the memory
+	 * limits.
+	 */
+	if (tc->flags & TALLOC_FLAG_POOLMEM) {
+		return;
+	}
+
+	/*
+	 * If we are part of a memory limited context hierarchy
+	 * we need to subtract the memory used from the counters
+	 */
+
+	talloc_memlimit_shrink(tc->limit, tc->size+TC_HDR_SIZE);
+
+	if (tc->limit->parent == tc) {
+		free(tc->limit);
+	}
+
+	tc->limit = NULL;
+}
+
+/*
+  Increase memory limit accounting after a malloc/realloc.
+*/
+static void talloc_memlimit_grow(struct talloc_memlimit *limit,
+				size_t size)
+{
+	struct talloc_memlimit *l;
+
+	for (l = limit; l != NULL; l = l->upper) {
+		size_t new_cur_size = l->cur_size + size;
+		if (new_cur_size < l->cur_size) {
+			talloc_abort("logic error in talloc_memlimit_grow\n");
+			return;
+		}
+		l->cur_size = new_cur_size;
+	}
+}
+
+/*
+  Decrease memory limit accounting after a free/realloc.
+*/
+static void talloc_memlimit_shrink(struct talloc_memlimit *limit,
+				size_t size)
+{
+	struct talloc_memlimit *l;
+
+	for (l = limit; l != NULL; l = l->upper) {
+		if (l->cur_size < size) {
+			talloc_abort("logic error in talloc_memlimit_shrink\n");
+			return;
+		}
+		l->cur_size = l->cur_size - size;
+	}
+}
+
 static bool talloc_memlimit_update(struct talloc_memlimit *limit,
 				   size_t old_size, size_t new_size)
 {
-- 
1.8.3.1


>From d2df8f410c05d899b30367daca3c0cfeb0ca9213 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:43:50 -0700
Subject: [PATCH 02/13] Remove magic TC_HDR_SIZE handling inside
 talloc_memlimit_check().

Callers already account for TC_HDR_SIZE, do not add it twice.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 067d46f..7b827ca 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -2561,7 +2561,7 @@ static bool talloc_memlimit_check(struct talloc_memlimit *limit, size_t size)
 	for (l = limit; l != NULL; l = l->upper) {
 		if (l->max_size != 0 &&
 		    ((l->max_size <= l->cur_size) ||
-		     (l->max_size - l->cur_size < TC_HDR_SIZE+size))) {
+		     (l->max_size - l->cur_size < size))) {
 			return false;
 		}
 	}
-- 
1.8.3.1


>From cd6146f6759aed41d41ccc44463502e86103d63b Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:46:09 -0700
Subject: [PATCH 03/13] Change _talloc_total_mem_internal() to ignore memory
 allocated from a pool when calculating limit size.

We must only count normal tallocs, or a talloc pool itself.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 7b827ca..1e25dfd 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -1817,7 +1817,14 @@ static size_t _talloc_total_mem_internal(const void *ptr,
 		break;
 	case TOTAL_MEM_LIMIT:
 		if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) {
-			total = tc->size + TC_HDR_SIZE;
+			/*
+			 * Don't count memory allocated from a pool
+			 * when calculating limits. Only count the
+			 * pool itself.
+			 */
+			if (!(tc->flags & TALLOC_FLAG_POOLMEM)) {
+				total = tc->size + TC_HDR_SIZE;
+			}
 		}
 		break;
 	}
-- 
1.8.3.1


>From dc73a0434d94cd5b2e5a6f34914aa6d4c9c48525 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:49:00 -0700
Subject: [PATCH 04/13] Change __talloc() to only call
 talloc_memlimit_check()/talloc_memlimit_grow() on actual malloc allocation.

Don't check the memlimit if the allocation was successful from a pool. We already
checked the memory limit when we created the pool.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 1e25dfd..cee7d23 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -586,27 +586,24 @@ static inline void *__talloc(const void *context, size_t size)
 			limit = ptc->limit;
 		}
 
-		if (!talloc_memlimit_check(limit, (TC_HDR_SIZE+size))) {
-			errno = ENOMEM;
-			return NULL;
-		}
-
 		tc = talloc_alloc_pool(ptc, TC_HDR_SIZE+size);
 	}
 
 	if (tc == NULL) {
+		/*
+		 * Only do the memlimit check/update on actual allocation.
+		 */
+		if (!talloc_memlimit_check(limit, TC_HDR_SIZE + size)) {
+			errno = ENOMEM;
+			return NULL;
+		}
+
 		tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
 		if (unlikely(tc == NULL)) return NULL;
 		tc->flags = TALLOC_MAGIC;
 		tc->pool  = NULL;
-	}
 
-	if (limit != NULL) {
-		struct talloc_memlimit *l;
-
-		for (l = limit; l != NULL; l = l->upper) {
-			l->cur_size += TC_HDR_SIZE+size;
-		}
+		talloc_memlimit_grow(limit, TC_HDR_SIZE + size);
 	}
 
 	tc->limit = limit;
-- 
1.8.3.1


>From 27c86dd93d716bf582b3eb37d91d8c1955ea13fa Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:51:20 -0700
Subject: [PATCH 05/13] Update memory limits when we call free() on a pool.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index cee7d23..c45ac93 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -807,6 +807,8 @@ static inline void _talloc_free_poolmem(struct talloc_chunk *tc,
 		 */
 		pool->hdr.c.name = location;
 
+		talloc_memlimit_update_on_free(&pool->hdr.c);
+
 		TC_INVALIDATE_FULL_CHUNK(&pool->hdr.c);
 		free(pool);
 		return;
-- 
1.8.3.1


>From 156e41c4d84562439f535a0ef66f424a88492641 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:54:38 -0700
Subject: [PATCH 06/13] Inside _talloc_free_internal(), always call
 talloc_memlimit_update_on_free() before we free the real memory.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 27 ++++-----------------------
 1 file changed, 4 insertions(+), 23 deletions(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index c45ac93..74eca3f 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -909,29 +909,6 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
 
 	tc->flags |= TALLOC_FLAG_FREE;
 
-	/*
-	 * If we are part of a memory limited context hierarchy
-	 * we need to subtract the memory used from the counters
-	 */
-	if (tc->limit) {
-		struct talloc_memlimit *l;
-
-		for (l = tc->limit; l != NULL; l = l->upper) {
-			if (l->cur_size >= tc->size+TC_HDR_SIZE) {
-				l->cur_size -= tc->size+TC_HDR_SIZE;
-			} else {
-				talloc_abort("cur_size memlimit counter not correct!");
-				return 0;
-			}
-		}
-
-		if (tc->limit->parent == tc) {
-			free(tc->limit);
-		}
-
-		tc->limit = NULL;
-	}
-
 	/* we mark the freed memory with where we called the free
 	 * from. This means on a double free error we can report where
 	 * the first free came from
@@ -952,6 +929,8 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
 			return 0;
 		}
 
+		talloc_memlimit_update_on_free(tc);
+
 		TC_INVALIDATE_FULL_CHUNK(tc);
 		free(tc);
 		return 0;
@@ -962,6 +941,8 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
 		return 0;
 	}
 
+	talloc_memlimit_update_on_free(tc);
+
 	TC_INVALIDATE_FULL_CHUNK(tc);
 	free(tc);
 	return 0;
-- 
1.8.3.1


>From 668dce6c900547d07b93fbf99ee65c246ce6bad8 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:57:43 -0700
Subject: [PATCH 07/13] In _talloc_steal_internal(), correctly decrement the
 memory limit in the source, and increment in the destination.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 74eca3f..54f3c0a 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -976,11 +976,8 @@ static void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
 
 		ctx_size = _talloc_total_limit_size(ptr, NULL, NULL);
 
-		if (!talloc_memlimit_update(tc->limit->upper, ctx_size, 0)) {
-			talloc_abort("cur_size memlimit counter not correct!");
-			errno = EINVAL;
-			return NULL;
-		}
+		/* Decrement the memory limit from the source .. */
+		talloc_memlimit_shrink(tc->limit->upper, ctx_size);
 
 		if (tc->limit->parent == tc) {
 			tc->limit->upper = NULL;
@@ -1028,13 +1025,9 @@ static void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
 	if (tc->limit || new_tc->limit) {
 		ctx_size = _talloc_total_limit_size(ptr, tc->limit,
 						    new_tc->limit);
-	}
-
-	if (new_tc->limit) {
-		struct talloc_memlimit *l;
-
-		for (l = new_tc->limit; l != NULL; l = l->upper) {
-			l->cur_size += ctx_size;
+		/* .. and increment it in the destination. */
+		if (new_tc->limit) {
+			talloc_memlimit_grow(new_tc->limit, ctx_size);
 		}
 	}
 
-- 
1.8.3.1


>From ae4fd624391367258c90c5d4c56cc30e98798465 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 12:59:04 -0700
Subject: [PATCH 08/13] Fix a conditional check. (size - tc->size > 0) is
 always true if size and tc->size are unsigned.

Replace with (size > tc->size).

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 54f3c0a..2683ff0 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -1507,7 +1507,7 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 		return NULL;
 	}
 
-	if (tc->limit && (size - tc->size > 0)) {
+	if (tc->limit && (size > tc->size)) {
 		if (!talloc_memlimit_check(tc->limit, (size - tc->size))) {
 			errno = ENOMEM;
 			return NULL;
-- 
1.8.3.1


>From 21f232676c2e71ef3c47150203ca7b4f9a1c9f79 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 13:03:27 -0700
Subject: [PATCH 09/13] Don't call talloc_memlimit_update() inside
 _talloc_realloc() when we're just manipulating pool members.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 2683ff0..aabd2fb 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -1629,14 +1629,6 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 		if (new_chunk_size == old_chunk_size) {
 			TC_UNDEFINE_GROW_CHUNK(tc, size);
 			tc->flags &= ~TALLOC_FLAG_FREE;
-			if (!talloc_memlimit_update(tc->limit,
-							tc->size, size)) {
-				talloc_abort("cur_size memlimit counter not"
-					     " correct!");
-				errno = EINVAL;
-				return NULL;
-			}
-
 			tc->size = size;
 			return ptr;
 		}
@@ -1652,13 +1644,6 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 			if (space_left >= space_needed) {
 				TC_UNDEFINE_GROW_CHUNK(tc, size);
 				tc->flags &= ~TALLOC_FLAG_FREE;
-				if (!talloc_memlimit_update(tc->limit,
-							tc->size, size)) {
-					talloc_abort("cur_size memlimit "
-						     "counter not correct!");
-					errno = EINVAL;
-					return NULL;
-				}
 				tc->size = size;
 				pool_tc->hdr.c.pool = tc_next_chunk(tc);
 				return ptr;
-- 
1.8.3.1


>From 83c0ae745b50970dae04b4c03fac32e712ae20dd Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 13:07:04 -0700
Subject: [PATCH 10/13] Inside _talloc_realloc(), keep track of size changes
 over malloc/realloc/free.

Replace the last use of talloc_memlimit_update() with talloc_memlimit_grow()/
talloc_memlimit_shrink().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index aabd2fb..66ac110 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -1479,6 +1479,8 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 	void *new_ptr;
 	bool malloced = false;
 	union talloc_pool_chunk *pool_tc = NULL;
+	size_t old_size = 0;
+	size_t new_size = 0;
 
 	/* size zero is equivalent to free() */
 	if (unlikely(size == 0)) {
@@ -1566,6 +1568,7 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 		if (new_ptr == NULL) {
 			new_ptr = malloc(TC_HDR_SIZE+size);
 			malloced = true;
+			new_size = size;
 		}
 
 		if (new_ptr) {
@@ -1573,6 +1576,9 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 			TC_INVALIDATE_FULL_CHUNK(tc);
 		}
 	} else {
+		/* We're doing malloc then free here, so record the difference. */
+		old_size = tc->size;
+		new_size = size;
 		new_ptr = malloc(size + TC_HDR_SIZE);
 		if (new_ptr) {
 			memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE);
@@ -1655,6 +1661,7 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 		if (new_ptr == NULL) {
 			new_ptr = malloc(TC_HDR_SIZE+size);
 			malloced = true;
+			new_size = size;
 		}
 
 		if (new_ptr) {
@@ -1664,6 +1671,9 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 		}
 	}
 	else {
+		/* We're doing realloc here, so record the difference. */
+		old_size = tc->size;
+		new_size = size;
 		new_ptr = realloc(tc, size + TC_HDR_SIZE);
 	}
 got_new_ptr:
@@ -1692,11 +1702,12 @@ got_new_ptr:
 		tc->next->prev = tc;
 	}
 
-	if (!talloc_memlimit_update(tc->limit, tc->size, size)) {
-		talloc_abort("cur_size memlimit counter not correct!");
-		errno = EINVAL;
-		return NULL;
+	if (new_size > old_size) {
+		talloc_memlimit_grow(tc->limit, new_size - old_size);
+	} else if (new_size < old_size) {
+		talloc_memlimit_shrink(tc->limit, old_size - new_size);
 	}
+
 	tc->size = size;
 	_talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
 
-- 
1.8.3.1


>From c0c1782179bc7dac0713396b5c45343bd370b6fe Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 13:08:33 -0700
Subject: [PATCH 11/13] Remove talloc_memlimit_update(). No longer used.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 24 ------------------------
 1 file changed, 24 deletions(-)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 66ac110..677ec0f 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -236,8 +236,6 @@ struct talloc_memlimit {
 };
 
 static bool talloc_memlimit_check(struct talloc_memlimit *limit, size_t size);
-static bool talloc_memlimit_update(struct talloc_memlimit *limit,
-				   size_t old_size, size_t new_size);
 static void talloc_memlimit_grow(struct talloc_memlimit *limit,
 				size_t size);
 static void talloc_memlimit_shrink(struct talloc_memlimit *limit,
@@ -2612,28 +2610,6 @@ static void talloc_memlimit_shrink(struct talloc_memlimit *limit,
 	}
 }
 
-static bool talloc_memlimit_update(struct talloc_memlimit *limit,
-				   size_t old_size, size_t new_size)
-{
-	struct talloc_memlimit *l;
-	ssize_t d;
-
-	if (old_size == 0) {
-		d = new_size + TC_HDR_SIZE;
-	} else {
-		d = new_size - old_size;
-	}
-	for (l = limit; l != NULL; l = l->upper) {
-		ssize_t new_cur_size = l->cur_size + d;
-		if (new_cur_size < 0) {
-			return false;
-		}
-		l->cur_size = new_cur_size;
-	}
-
-	return true;
-}
-
 _PUBLIC_ int talloc_set_memlimit(const void *ctx, size_t max_size)
 {
 	struct talloc_chunk *tc = talloc_chunk_from_ptr(ctx);
-- 
1.8.3.1


>From 591a73cee761960bce92fcd1d1daf7cc7c599748 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 13:09:03 -0700
Subject: [PATCH 12/13] Add simple limited pool tests to test_memlimit().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/testsuite.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/lib/talloc/testsuite.c b/lib/talloc/testsuite.c
index d456cbb..426c31a 100644
--- a/lib/talloc/testsuite.c
+++ b/lib/talloc/testsuite.c
@@ -1359,6 +1359,8 @@ static bool test_memlimit(void)
 {
 	void *root;
 	char *l1, *l2, *l3, *l4, *l5, *t;
+	char *pool;
+	int i;
 
 	printf("test: memlimit\n# MEMORY LIMITS\n");
 
@@ -1520,6 +1522,31 @@ static bool test_memlimit(void)
 	talloc_report_full(root, stdout);
 	talloc_free(root);
 
+	/* Test memlimits with pools. */
+	pool = talloc_pool(NULL, 10*1024);
+	torture_assert("memlimit", pool != NULL,
+		"failed: alloc should not fail due to memory limit\n");
+	talloc_set_memlimit(pool, 10*1024);
+	for (i = 0; i < 9; i++) {
+		l1 = talloc_size(pool, 1024);
+		torture_assert("memlimit", l1 != NULL,
+			"failed: alloc should not fail due to memory limit\n");
+	}
+	/* The next alloc should fail. */
+	l2 = talloc_size(pool, 1024);
+	torture_assert("memlimit", l2 == NULL,
+			"failed: alloc should fail due to memory limit\n");
+
+	/* Moving one of the children shouldn't change the limit,
+	   as it's still inside the pool. */
+	root = talloc_new(NULL);
+	talloc_steal(root, l1);
+	l2 = talloc_size(pool, 1024);
+	torture_assert("memlimit", l2 == NULL,
+			"failed: alloc should fail due to memory limit\n");
+
+	talloc_free(pool);
+	talloc_free(root);
 	printf("success: memlimit\n");
 
 	return true;
-- 
1.8.3.1


>From 310367d1d022cb90b99c25275db98f1d1295b0dc Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 27 Aug 2013 13:20:43 -0700
Subject: [PATCH 13/13] Fix valgrind errors with memmove and talloc pools.

bin/smbtorture //127.0.0.1 local.talloc now runs with no valgrind errors.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/talloc/talloc.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c
index 677ec0f..69d5a16 100644
--- a/lib/talloc/talloc.c
+++ b/lib/talloc/talloc.c
@@ -1609,6 +1609,27 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 				size_t old_used = TC_HDR_SIZE + tc->size;
 				size_t new_used = TC_HDR_SIZE + size;
 				new_ptr = start;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+				{
+					/*
+					 * The area from
+					 * start -> tc may have
+					 * been freed and thus been marked as
+					 * VALGRIND_MEM_NOACCESS. Set it to
+					 * VALGRIND_MEM_UNDEFINED so we can
+					 * copy into it without valgrind errors.
+					 * We can't just mark
+					 * new_ptr -> new_ptr + old_used
+					 * as this may overlap on top of tc,
+					 * (which is why we use memmove, not
+					 * memcpy below) hence the MIN.
+					 */
+					size_t undef_len = MIN((((char *)tc) - ((char *)new_ptr)),old_used);
+					VALGRIND_MAKE_MEM_UNDEFINED(new_ptr, undef_len);
+				}
+#endif
+
 				memmove(new_ptr, tc, old_used);
 
 				tc = (struct talloc_chunk *)new_ptr;
-- 
1.8.3.1



More information about the samba-technical mailing list