lib/blob: do not assume realloc(NULL, 0) returns a not-NULL value

There is situation that num_extent_pages is zero and original pointer is
also NULL, the realloc() could return a Not NULL pointer.

Related UT has been added and updated.
1) In the default allocation (num_clusters == 0), the extent_pages is not allocated as expected.
2) In the thin provisioning allocation (num_clusters != 0), the extent_pages will be allocated if extent_table is used.

More related information as below:

The crux of the problem is that according to POSIX:

realloc: "If ptr is NULL, then the call is equivalent to malloc(size)"
malloc: "If size is 0, then malloc returns either NULL or a unique pointer value that can later be successfully passed to free"

blobstore was relying on realloc(NULL, 0) always return a unique pointer value, and not NULL.  This is not portable behavior.

Change-Id: Ibc28d9696f15a3c0e2aa6bb2371dc23576c28954
Signed-off-by: GangCao <gang.cao@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/10470
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
GangCao 2021-11-30 21:01:56 -05:00 committed by Jim Harris
parent a4d132cdb5
commit 10f32b9f19
2 changed files with 71 additions and 6 deletions

View File

@ -669,22 +669,24 @@ blob_parse_page(const struct spdk_blob_md_page *page, struct spdk_blob *blob)
return -EINVAL;
}
blob->extent_table_found = true;
if (desc_extent_table->length == 0 ||
(extent_pages_length % sizeof(desc_extent_table->extent_page[0]) != 0)) {
return -EINVAL;
}
blob->extent_table_found = true;
for (i = 0; i < extent_pages_length / sizeof(desc_extent_table->extent_page[0]); i++) {
num_extent_pages += desc_extent_table->extent_page[i].num_pages;
}
tmp = realloc(blob->active.extent_pages, num_extent_pages * sizeof(uint32_t));
if (tmp == NULL) {
return -ENOMEM;
if (num_extent_pages > 0) {
tmp = realloc(blob->active.extent_pages, num_extent_pages * sizeof(uint32_t));
if (tmp == NULL) {
return -ENOMEM;
}
blob->active.extent_pages = tmp;
}
blob->active.extent_pages = tmp;
blob->active.extent_pages_array_size = num_extent_pages;
blob->remaining_clusters_in_et = desc_extent_table->num_clusters;

View File

@ -442,6 +442,57 @@ blob_create(void)
CU_ASSERT(g_bserrno == -ENOSPC);
}
static void
blob_create_zero_extent(void)
{
struct spdk_blob_store *bs = g_bs;
struct spdk_blob *blob;
spdk_blob_id blobid;
/* Create blob with default options (opts == NULL) */
spdk_bs_create_blob_ext(bs, NULL, blob_op_with_id_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID);
blobid = g_blobid;
spdk_bs_open_blob(bs, blobid, blob_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
blob = g_blob;
CU_ASSERT(spdk_blob_get_num_clusters(blob) == 0);
CU_ASSERT(blob->extent_table_found == true);
CU_ASSERT(blob->active.extent_pages_array_size == 0);
CU_ASSERT(blob->active.extent_pages == NULL);
spdk_blob_close(blob, blob_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
/* Create blob with NULL internal options */
bs_create_blob(bs, NULL, NULL, blob_op_with_id_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID);
blobid = g_blobid;
spdk_bs_open_blob(bs, blobid, blob_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
blob = g_blob;
CU_ASSERT(TAILQ_FIRST(&blob->xattrs_internal) == NULL);
CU_ASSERT(spdk_blob_get_num_clusters(blob) == 0);
CU_ASSERT(blob->extent_table_found == true);
CU_ASSERT(blob->active.extent_pages_array_size == 0);
CU_ASSERT(blob->active.extent_pages == NULL);
spdk_blob_close(blob, blob_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
}
/*
* Create and delete one blob in a loop over and over again. This helps ensure
* that the internal bit masks tracking used clusters and md_pages are being
@ -588,6 +639,7 @@ blob_create_internal(void)
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
CU_ASSERT(TAILQ_FIRST(&g_blob->xattrs_internal) == NULL);
CU_ASSERT(spdk_blob_get_num_clusters(g_blob) == 0);
blob = g_blob;
@ -627,6 +679,16 @@ blob_thin_provision(void)
blob = ut_blob_create_and_open(bs, &opts);
blobid = spdk_blob_get_id(blob);
CU_ASSERT(blob->invalid_flags & SPDK_BLOB_THIN_PROV);
/* In thin provisioning with num_clusters is set, if not using the
* extent table, there is no allocation. If extent table is used,
* there is related allocation happened. */
if (blob->extent_table_found == true) {
CU_ASSERT(blob->active.extent_pages_array_size > 0);
CU_ASSERT(blob->active.extent_pages != NULL);
} else {
CU_ASSERT(blob->active.extent_pages_array_size == 0);
CU_ASSERT(blob->active.extent_pages == NULL);
}
spdk_blob_close(blob, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
@ -7104,6 +7166,7 @@ int main(int argc, char **argv)
CU_ADD_TEST(suite_bs, blob_create_loop);
CU_ADD_TEST(suite_bs, blob_create_fail);
CU_ADD_TEST(suite_bs, blob_create_internal);
CU_ADD_TEST(suite_bs, blob_create_zero_extent);
CU_ADD_TEST(suite, blob_thin_provision);
CU_ADD_TEST(suite_bs, blob_snapshot);
CU_ADD_TEST(suite_bs, blob_clone);