From 8159347d05eb93cf7c0997f0db57f40c45a184cf Mon Sep 17 00:00:00 2001
From: Nikolay Shirokovskiy <nshirokovskiy@tarantool.org>
Date: Thu, 21 Sep 2023 12:56:14 +0300
Subject: [PATCH] misc: avoid allocations of size 0 for region

Regular region implementation supports allocations of size 0 with no
extra efforts. It returns a non-NULL pointer in this case. However in
case of ASAN friendly implementation it will require a special care for
this case. Instead let's avaid allocations if size 0 for region.

Also use xregion_ macros for allocations. Our current policy is to panic
on OOM on runtime allocations.

Part of tarantool/tarantool#7327

NO_TEST=internal
NO_CHANGELOG=internal
NO_DOC=internal
---
 src/box/alter.cc            | 14 +++++---------
 src/box/index_def.c         | 12 ++++--------
 src/box/index_def.h         |  3 +--
 src/box/lua/key_def.c       |  9 ++-------
 src/box/memtx_space.c       |  4 ----
 src/box/service_engine.c    |  4 ----
 src/box/sql/build.c         |  8 ++++++--
 src/box/sql/func.c          |  4 +++-
 src/box/sql/select.c        | 11 ++++++++---
 src/box/sysview.c           |  4 ----
 src/box/vinyl.c             | 13 ++++---------
 src/box/vy_point_lookup.c   | 11 ++++-------
 src/box/xrow_update.c       | 12 ++++++++----
 src/lib/mpstream/mpstream.c |  3 ++-
 14 files changed, 47 insertions(+), 65 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index bcd2a1b544..4823ae7b40 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -2460,16 +2460,12 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event)
 		 * these fields become optional - index
 		 * comparators must be updated.
 		 */
-		struct key_def **keys;
-		size_t bsize;
+		struct key_def **keys = NULL;
 		RegionGuard region_guard(&fiber()->gc);
-		keys = region_alloc_array(&fiber()->gc, typeof(keys[0]),
-					  old_space->index_count, &bsize);
-		if (keys == NULL) {
-			diag_set(OutOfMemory, bsize, "region_alloc_array",
-				 "keys");
-			return -1;
-		}
+		if (old_space->index_count > 0)
+			keys = xregion_alloc_array(&fiber()->gc,
+						   typeof(keys[0]),
+						   old_space->index_count);
 		for (uint32_t i = 0; i < old_space->index_count; ++i)
 			keys[i] = old_space->index[i]->def->key_def;
 		alter->new_min_field_count =
diff --git a/src/box/index_def.c b/src/box/index_def.c
index 20e51254e5..60926ebc1c 100644
--- a/src/box/index_def.c
+++ b/src/box/index_def.c
@@ -252,15 +252,11 @@ index_def_to_key_def(struct rlist *index_defs, int *size)
 	struct index_def *index_def;
 	rlist_foreach_entry(index_def, index_defs, link)
 		key_count++;
-	size_t bsize;
-	struct key_def **keys =
-		region_alloc_array(&fiber()->gc, typeof(keys[0]), key_count,
-				   &bsize);
-	if (keys == NULL) {
-		diag_set(OutOfMemory, bsize, "region_alloc_array", "keys");
-		return NULL;
-	}
 	*size = key_count;
+	if (key_count == 0)
+		return NULL;
+	struct key_def **keys =
+		xregion_alloc_array(&fiber()->gc, typeof(keys[0]), key_count);
 	key_count = 0;
 	rlist_foreach_entry(index_def, index_defs, link)
 		keys[key_count++] = index_def->key_def;
diff --git a/src/box/index_def.h b/src/box/index_def.h
index 05ab1fc33a..ab57088287 100644
--- a/src/box/index_def.h
+++ b/src/box/index_def.h
@@ -398,8 +398,7 @@ index_def_new(uint32_t space_id, uint32_t iid, const char *name,
  *
  * @param index_defs List head.
  * @param[out] size  Array size.
- * @retval not NULL  Array of pointers to key_def
- * @retval NULL      Memory error.
+ * @retval Array of pointers to key_def (NULL if size == 0).
  */
 struct key_def **
 index_def_to_key_def(struct rlist *index_defs, int *size);
diff --git a/src/box/lua/key_def.c b/src/box/lua/key_def.c
index 2ea239f4a6..80c7d2d28a 100644
--- a/src/box/lua/key_def.c
+++ b/src/box/lua/key_def.c
@@ -494,18 +494,13 @@ lbox_key_def_new(struct lua_State *L)
 
 	struct region *region = &fiber()->gc;
 	size_t region_svp = region_used(region);
-	size_t size;
-	struct key_part_def *parts =
-		region_alloc_array(region, typeof(parts[0]), part_count, &size);
-	if (parts == NULL) {
-		diag_set(OutOfMemory, size, "region_alloc_array", "parts");
-		return luaT_error(L);
-	}
 	if (part_count == 0) {
 		diag_set(IllegalParams, "Key definition can only be constructed"
 					" by using at least 1 key_part");
 		return luaT_error(L);
 	}
+	struct key_part_def *parts =
+		xregion_alloc_array(region, typeof(parts[0]), part_count);
 
 	for (uint32_t i = 0; i < part_count; ++i) {
 		lua_pushinteger(L, i + 1);
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 04476eeca8..67bfa0b632 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -1416,10 +1416,6 @@ memtx_space_new(struct memtx_engine *memtx,
 	int key_count = 0;
 	size_t region_svp = region_used(&fiber()->gc);
 	struct key_def **keys = index_def_to_key_def(key_list, &key_count);
-	if (keys == NULL) {
-		free(memtx_space);
-		return NULL;
-	}
 	struct tuple_format *format =
 		space_tuple_format_new(&memtx_tuple_format_vtab,
 				       memtx, keys, key_count, def);
diff --git a/src/box/service_engine.c b/src/box/service_engine.c
index 65b48f2f29..af48c36dba 100644
--- a/src/box/service_engine.c
+++ b/src/box/service_engine.c
@@ -65,10 +65,6 @@ service_engine_create_space(struct engine *engine, struct space_def *def,
 	int key_count = 0;
 	size_t region_svp = region_used(&fiber()->gc);
 	struct key_def **keys = index_def_to_key_def(key_list, &key_count);
-	if (keys == NULL) {
-		free(space);
-		return NULL;
-	}
 	struct tuple_format *format =
 		space_tuple_format_new(&tuple_format_runtime->vtab,
 				       NULL, keys, key_count, def);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index cf4cacf611..0d032f2f16 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -276,8 +276,12 @@ sql_shallow_space_copy(struct Parse *parse, struct space *space)
 	struct space *ret = sql_template_space_new(parse, space->def->name);
 	ret->index_count = space->index_count;
 	ret->index_id_max = space->index_id_max;
-	ret->index = xregion_alloc_array(&parse->region, typeof(struct index *),
-					 space->index_count);
+	if (space->index_count > 0)
+		ret->index = xregion_alloc_array(&parse->region,
+						 struct index *,
+						 space->index_count);
+	else
+		ret->index = NULL;
 	memcpy(ret->index, space->index,
 	       sizeof(struct index *) * space->index_count);
 	memcpy(ret->def, space->def, sizeof(struct space_def));
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index e95118b778..4693eececb 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -517,7 +517,9 @@ func_trim_str(struct sql_context *ctx, int argc, const struct Mem *argv)
 
 	struct region *region = &fiber()->gc;
 	size_t svp = region_used(region);
-	uint8_t *chars_len = xregion_alloc(region, chars_size);
+	uint8_t *chars_len = NULL;
+	if (chars_size > 0)
+		chars_len = xregion_alloc(region, chars_size);
 	size_t chars_count = 0;
 
 	int32_t offset = 0;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 71c53b107c..5649e36e25 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -2054,9 +2054,14 @@ sqlColumnsFromExprList(Parse * parse, ExprList * expr_list,
 	 */
 	assert(space_def->fields == NULL);
 	struct region *region = &parse->region;
-	space_def->fields =
-		xregion_alloc_array(region, typeof(space_def->fields[0]),
-				    column_count);
+	if (column_count > 0) {
+		space_def->fields =
+			xregion_alloc_array(region,
+					    typeof(space_def->fields[0]),
+					    column_count);
+	} else {
+		space_def->fields = NULL;
+	}
 	for (uint32_t i = 0; i < column_count; i++) {
 		memcpy(&space_def->fields[i], &field_def_default,
 		       sizeof(field_def_default));
diff --git a/src/box/sysview.c b/src/box/sysview.c
index 6b3dfd790d..77e21233c9 100644
--- a/src/box/sysview.c
+++ b/src/box/sysview.c
@@ -538,10 +538,6 @@ sysview_engine_create_space(struct engine *engine, struct space_def *def,
 	 */
 	size_t region_svp = region_used(&fiber()->gc);
 	struct key_def **keys = index_def_to_key_def(key_list, &key_count);
-	if (keys == NULL) {
-		free(space);
-		return NULL;
-	}
 	struct tuple_format *format =
 		space_tuple_format_new(NULL, NULL, keys, key_count, def);
 	region_truncate(&fiber()->gc, region_svp);
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 6a5c1b1980..17c014e94a 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -622,16 +622,11 @@ vinyl_engine_create_space(struct engine *engine, struct space_def *def,
 	struct index_def *index_def;
 	rlist_foreach_entry(index_def, key_list, link)
 		key_count++;
-	struct key_def **keys;
-	size_t size;
+	struct key_def **keys = NULL;
 	size_t region_svp = region_used(&fiber()->gc);
-	keys = region_alloc_array(&fiber()->gc, typeof(keys[0]), key_count,
-				  &size);
-	if (keys == NULL) {
-		diag_set(OutOfMemory, size, "region_alloc_array", "keys");
-		free(space);
-		return NULL;
-	}
+	if (key_count > 0)
+		keys = xregion_alloc_array(&fiber()->gc, typeof(keys[0]),
+					   key_count);
 	key_count = 0;
 	rlist_foreach_entry(index_def, key_list, link)
 		keys[key_count++] = index_def->key_def;
diff --git a/src/box/vy_point_lookup.c b/src/box/vy_point_lookup.c
index 119a8d2028..af625dae25 100644
--- a/src/box/vy_point_lookup.c
+++ b/src/box/vy_point_lookup.c
@@ -185,15 +185,12 @@ vy_point_lookup_scan_slices(struct vy_lsm *lsm, const struct vy_read_view **rv,
 							   ITER_EQ, key);
 	assert(range != NULL);
 	int slice_count = range->slice_count;
-	size_t size;
 	size_t region_svp = region_used(&fiber()->gc);
+	if (slice_count == 0)
+		return 0;
 	struct vy_slice **slices =
-		region_alloc_array(&fiber()->gc, typeof(slices[0]), slice_count,
-				   &size);
-	if (slices == NULL) {
-		diag_set(OutOfMemory, size, "region_alloc_array", "slices");
-		return -1;
-	}
+		xregion_alloc_array(&fiber()->gc, typeof(slices[0]),
+				    slice_count);
 	int i = 0;
 	struct vy_slice *slice;
 	rlist_foreach_entry(slice, &range->slices, in_range) {
diff --git a/src/box/xrow_update.c b/src/box/xrow_update.c
index 9b3217a6a8..81a5dd43aa 100644
--- a/src/box/xrow_update.c
+++ b/src/box/xrow_update.c
@@ -167,10 +167,14 @@ xrow_update_read_ops(struct xrow_update *update, const char *expr,
 		return -1;
 	}
 
-	int size = update->op_count * sizeof(update->ops[0]);
-	update->ops = (struct xrow_update_op *)
-		xregion_aligned_alloc(&fiber()->gc, size,
-				      alignof(struct xrow_update_op));
+	if (update->op_count > 0) {
+		update->ops = (struct xrow_update_op *)
+			xregion_alloc_array(&fiber()->gc,
+					    typeof(update->ops[0]),
+					    update->op_count);
+	} else {
+		update->ops = NULL;
+	}
 	struct xrow_update_op *op = update->ops;
 	struct xrow_update_op *ops_end = op + update->op_count;
 	for (int i = 1; op < ops_end; op++, i++) {
diff --git a/src/lib/mpstream/mpstream.c b/src/lib/mpstream/mpstream.c
index 0c4274bf63..3cff2dad96 100644
--- a/src/lib/mpstream/mpstream.c
+++ b/src/lib/mpstream/mpstream.c
@@ -29,7 +29,8 @@ mpstream_panic_cb(void *error_ctx)
 void
 mpstream_reserve_slow(struct mpstream *stream, size_t size)
 {
-	stream->alloc(stream->ctx, stream->pos - stream->buf);
+	if (stream->pos != stream->buf)
+		stream->alloc(stream->ctx, stream->pos - stream->buf);
 	stream->buf = stream->reserve(stream->ctx, &size);
 	if (stream->buf == NULL) {
 		diag_set(OutOfMemory, size, "mpstream", "reserve");
-- 
GitLab