diff --git a/core/palloc.m b/core/palloc.m
index 3dfe17b2ddbb51060c96fee0597f8d4976b0afbd..bc9c5a2fd95c1912759d6c1bb1f940eea867921e 100644
--- a/core/palloc.m
+++ b/core/palloc.m
@@ -269,7 +269,7 @@ palloc(struct palloc_pool *restrict pool, size_t size)
 		ptr = palloc_slow_path(pool, rz_size);
 
 	assert(poisoned(ptr + PALLOC_REDZONE, size) == NULL);
-	VALGRIND_MEMPOOL_ALLOC(pool, ptr + PALLOC_REDZONE, size);
+	VALGRIND_MAKE_MEM_DEFINED(ptr + PALLOC_REDZONE, size);
 
 	return ptr + PALLOC_REDZONE;
 }
@@ -293,25 +293,65 @@ palloca(struct palloc_pool *pool, size_t size, size_t align)
 	return (void *)TYPEALIGN(align, (uintptr_t)ptr);
 }
 
+
+static inline void
+chunk_free(struct chunk *chunk)
+{
+	if (chunk->size <= palloc_greatest_size()) {
+		chunk->free = chunk->size - sizeof(struct chunk);
+		chunk->brk = (void *)chunk + sizeof(struct chunk);
+		SLIST_INSERT_HEAD(&chunk->class->chunks, chunk, free_link);
+		poison_chunk(chunk);
+	} else {
+		free(chunk);
+	}
+}
+
+/**
+ * Release all memory down to new_size; new_size has to be previously
+ * obtained by calling palloc_allocated().
+ */
+void
+ptruncate(struct palloc_pool *pool, size_t new_size)
+{
+	assert(new_size <= pool->allocated);
+
+	ssize_t cut_size = pool->allocated - new_size;
+	struct chunk *chunk = SLIST_FIRST(&pool->chunks);
+
+	for (chunk = SLIST_FIRST(&pool->chunks); chunk;
+	     chunk = SLIST_FIRST(&pool->chunks)) {
+
+		size_t chunk_used = (chunk->size - chunk->free -
+				     sizeof(struct chunk));
+		if (chunk_used > cut_size) {
+			/* This is the last chunk to trim. */
+			chunk->brk -= cut_size;
+			chunk->free += cut_size;
+			VALGRIND_MAKE_MEM_NOACCESS(chunk->brk, cut_size);
+			cut_size = 0;
+			break;
+		}
+		cut_size -= chunk_used;
+		/* Remove the entire chunk. */
+		SLIST_REMOVE_HEAD(&pool->chunks, busy_link);
+		chunk_free(chunk);
+	}
+	assert(cut_size == 0);
+	pool->allocated = new_size;
+}
+
 void
 prelease(struct palloc_pool *pool)
 {
 	struct chunk *chunk, *next_chunk;
 
-	for (chunk = SLIST_FIRST(&pool->chunks); chunk != NULL; chunk = next_chunk) {
+	for (chunk = SLIST_FIRST(&pool->chunks); chunk; chunk = next_chunk) {
 		next_chunk = SLIST_NEXT(chunk, busy_link);
-		if (chunk->size <= palloc_greatest_size()) {
-			chunk->free = chunk->size - sizeof(struct chunk);
-			chunk->brk = (void *)chunk + sizeof(struct chunk);
-			SLIST_INSERT_HEAD(&chunk->class->chunks, chunk, free_link);
-			poison_chunk(chunk);
-		} else {
-			free(chunk);
-		}
+		chunk_free(chunk);
 	}
 
 	SLIST_INIT(&pool->chunks);
-	VALGRIND_MEMPOOL_TRIM(pool, NULL, 0);
 	pool->allocated = 0;
 }
 
@@ -331,7 +371,6 @@ palloc_create_pool(const char *name)
 	palloc_set_name(pool, name);
 	SLIST_INIT(&pool->chunks);
 	SLIST_INSERT_HEAD(&pools, pool, link);
-	VALGRIND_CREATE_MEMPOOL(pool, PALLOC_REDZONE, 0);
 	return pool;
 }
 
@@ -340,7 +379,6 @@ palloc_destroy_pool(struct palloc_pool *pool)
 {
 	SLIST_REMOVE(&pools, pool, palloc_pool, link);
 	prelease(pool);
-	VALGRIND_DESTROY_MEMPOOL(pool);
 	free(pool);
 }
 
diff --git a/include/palloc.h b/include/palloc.h
index a6b4425a8965cec4e928b87640eafbbf2968cc73..b93722ce6cf57c5310be5d3bc2c858c64b1a12c9 100644
--- a/include/palloc.h
+++ b/include/palloc.h
@@ -40,6 +40,7 @@ void *palloc(struct palloc_pool *pool, size_t size) __attribute__((regparm(2)));
 void *p0alloc(struct palloc_pool *pool, size_t size) __attribute__((regparm(2)));
 void *palloca(struct palloc_pool *pool, size_t size, size_t align);
 void prelease(struct palloc_pool *pool);
+void ptruncate(struct palloc_pool *pool, size_t sz);
 void prelease_after(struct palloc_pool *pool, size_t after);
 struct palloc_pool *palloc_create_pool(const char *name);
 void palloc_destroy_pool(struct palloc_pool *);
diff --git a/mod/box/box_lua.m b/mod/box/box_lua.m
index 9507d173e9ccce6e47d3cb49c6c4fa271aebf321..eb643f1899e1523343741143ebf8f1f81d73992e 100644
--- a/mod/box/box_lua.m
+++ b/mod/box/box_lua.m
@@ -646,11 +646,13 @@ static int lbox_process(lua_State *L)
 	}
 	int top = lua_gettop(L); /* to know how much is added by rw_callback */
 
+	size_t allocated_size = palloc_allocated(fiber->gc_pool);
 	struct box_txn *old_txn = txn_enter_lua(L);
 	@try {
 		rw_callback(op, &req);
 	} @finally {
 		fiber->mod_data.txn = old_txn;
+		ptruncate(fiber->gc_pool, allocated_size);
 	}
 	return lua_gettop(L) - top;
 }
diff --git a/test/box/lua.result b/test/box/lua.result
index 0fbc3a9738552decfeeede1560e1ed6e90fd5458..149674fdf354cc985e9096b6c4b9c0c0fb775675 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -814,6 +814,9 @@ Found 25 tuples:
 lua box.space[0]:truncate()
 ---
 ...
+lua box.crossjoin = nil
+---
+...
 
 # A test case for Bug#901674
 # No way to inspect exceptions from Box in Lua
diff --git a/test/box/lua.test b/test/box/lua.test
index 627bec1ba608b07bd816e7a33d8ebfed5cabb94f..3cca574787fd90de592db22817243baf45dd62f2 100644
--- a/test/box/lua.test
+++ b/test/box/lua.test
@@ -237,6 +237,7 @@ exec admin "lua box.space[0]:insert(4, 'world')"
 exec admin "lua box.space[0]:insert(5, 'hello world')"
 exec sql "call box.crossjoin('0', '0', '10000')"
 exec admin "lua box.space[0]:truncate()"
+exec admin "lua box.crossjoin = nil"
 print """
 # A test case for Bug#901674
 # No way to inspect exceptions from Box in Lua