Skip to content

Commit 5b90bf7

Browse files
authored
Merge pull request #2624 from simonqhughes/master
CFSTORE Bugfix for realloc() moving KV area and cfstore_file_t data structures not being updated correctly
2 parents da5a19f + b2f561a commit 5b90bf7

File tree

9 files changed

+662
-218
lines changed

9 files changed

+662
-218
lines changed

features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545

4646
using namespace utest::v1;
4747

48+
#define CFSTORE_ADD_DEL_MALLOC_SIZE 1024
49+
4850
static char cfstore_add_del_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE];
4951

5052
#ifdef YOTTA_CFG_CFSTORE_UVISOR
@@ -277,11 +279,104 @@ control_t cfstore_add_del_test_04(const size_t call_count)
277279
return CaseNext;
278280
}
279281

282+
/** @brief Delete and attribute after an internal realloc of the cfstore memory area
283+
*
284+
* This test case goes through the following steps:
285+
* 1. Creates attribute att_1 of size x, and write some data. This causes an internal
286+
* cfstore realloc to allocate heap memory for the attribute.
287+
* 2. Allocates some memory on the heap. Typically, this will be immediately after the
288+
* memory used by cfstore for the KV area. This means that if any cfstore reallocs are
289+
* made to increase size the memory area will have to move.
290+
* 3. Creates attribute att_2 of size y. This causes an internal cfstore realloc to move
291+
* the KV memory area to a new location.
292+
* 4. Delete att_1. This causes an internal realloc to shrink the area and tests that the
293+
* internal data structures that contain pointers to different parts of the KV area
294+
* are updated correctly.
295+
* 5. Allocates some memory on the heap. If the heap has been corrupted, this will likely trigger
296+
* a crash
297+
*
298+
* @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
299+
*/
300+
control_t cfstore_add_del_test_05_end(const size_t call_count)
301+
{
302+
char data[] = "some test data";
303+
char filename[] = "file1";
304+
char filename2[] = "file2";
305+
int32_t ret = ARM_DRIVER_ERROR;
306+
void *test_buf1 = NULL;
307+
void *test_buf2 = NULL;
308+
ARM_CFSTORE_DRIVER *cfstoreDriver = &cfstore_driver;
309+
ARM_CFSTORE_KEYDESC keyDesc1;
310+
ARM_CFSTORE_HANDLE_INIT(hkey1);
311+
ARM_CFSTORE_KEYDESC keyDesc2;
312+
ARM_CFSTORE_HANDLE_INIT(hkey2);
313+
ARM_CFSTORE_SIZE count = sizeof(data);
314+
315+
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
316+
(void) call_count;
317+
318+
/* step 1 */
319+
memset(&keyDesc1, 0, sizeof(keyDesc1));
320+
keyDesc1.drl = ARM_RETENTION_NVM;
321+
keyDesc1.flags.read = true;
322+
keyDesc1.flags.write = true;
323+
ret = cfstoreDriver->Create(filename, 1024, &keyDesc1, hkey1);
324+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create attribute 1 (ret=%d)\n", __func__, (int) ret);
325+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g);
326+
327+
/* Write data to file */
328+
ret = cfstoreDriver->Write(hkey1, (const char *)data, &count);
329+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write to attribute 1 (ret=%d)\n", __func__, (int) ret);
330+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g);
331+
332+
/* step 2 */
333+
test_buf1 = malloc(CFSTORE_ADD_DEL_MALLOC_SIZE);
334+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocate memory (test_buf1=%p)\n", __func__, test_buf1);
335+
TEST_ASSERT_MESSAGE(test_buf1 != NULL, cfstore_add_del_utest_msg_g);
336+
337+
/* step 3 */
338+
memset(&keyDesc2, 0, sizeof(keyDesc2));
339+
keyDesc2.drl = ARM_RETENTION_NVM;
340+
keyDesc2.flags.read = true;
341+
keyDesc2.flags.write = true;
342+
ret = cfstoreDriver->Create(filename2, 1024, &keyDesc2, hkey2);
343+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create attribute 2 (ret=%d)\n", __func__, (int) ret);
344+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g);
345+
346+
/* Write data to file */
347+
count = sizeof(data);
348+
ret = cfstoreDriver->Write(hkey2, (const char *)data, &count);
349+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write to attribute 2 (ret=%d)\n", __func__, (int) ret);
350+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g);
351+
352+
/* step 4 */
353+
ret = cfstoreDriver->Delete(hkey1);
354+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete to attribute 1 (ret=%d)\n", __func__, (int) ret);
355+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g);
356+
357+
ret = cfstoreDriver->Close(hkey1);
358+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close to attribute 1 (ret=%d)\n", __func__, (int) ret);
359+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g);
360+
361+
/* step 5 */
362+
test_buf2 = malloc(CFSTORE_ADD_DEL_MALLOC_SIZE);
363+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocate memory (test_buf2=%p)\n", __func__, test_buf2);
364+
TEST_ASSERT_MESSAGE(test_buf2 != NULL, cfstore_add_del_utest_msg_g);
365+
366+
/* clean up */
367+
ret = cfstoreDriver->Close(hkey2);
368+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close to attribute 2 (ret=%d)\n", __func__, (int) ret);
369+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g);
370+
free(test_buf2);
371+
free(test_buf1);
372+
373+
return CaseNext;
374+
}
280375

281376
/// @cond CFSTORE_DOXYGEN_DISABLE
282377
utest::v1::status_t greentea_setup(const size_t number_of_cases)
283378
{
284-
GREENTEA_SETUP(100, "default_auto");
379+
GREENTEA_SETUP(300, "default_auto");
285380
return greentea_test_setup_handler(number_of_cases);
286381
}
287382

@@ -296,6 +391,8 @@ Case cases[] = {
296391
Case("ADD_DEL_test_03_start", cfstore_utest_default_start),
297392
Case("ADD_DEL_test_03_end", cfstore_add_del_test_03_end),
298393
Case("ADD_DEL_test_04", cfstore_add_del_test_04),
394+
Case("ADD_DEL_test_05_start", cfstore_utest_default_start),
395+
Case("ADD_DEL_test_05_end", cfstore_add_del_test_05_end),
299396
};
300397

301398

features/storage/FEATURE_STORAGE/TESTS/cfstore/close/close.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ static control_t cfstore_close_test_00(const size_t call_count)
7575
*
7676
* The is a basic test case which does the following:
7777
* - 01. create a key with handle hkey1
78-
* - 02. write data of hkey
78+
* - 02. write data of hkey1
7979
* - 03. opens KV with 2nd handle, hkey2
8080
* - 04. read data with hkey2 and make sure its the same as that written with hkey1
8181
* - 05. write new data with hkey2
8282
* - 06. delete hkey2
8383
* - 07. close hkey2
8484
* - 08. read hkey1 and make sure the data is the newly written data i.e. the key hasnt
85-
* been deleted yet
85+
* been deleted yet
8686
* - 09. try to open KV and make sure unable to do so, as KV is deleting
8787
* - 10. close hkey1
8888
* - 11. try to open KV and make sure unable to do so because its now been deleted

features/storage/FEATURE_STORAGE/TESTS/cfstore/create/create.cpp

Lines changed: 163 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ using namespace utest::v1;
5151
#else
5252
#define CFSTORE_CREATE_GREENTEA_TIMEOUT_S 60
5353
#endif
54+
#define CFSTORE_CREATE_MALLOC_SIZE 1024
5455

5556
static char cfstore_create_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE];
5657

@@ -272,15 +273,14 @@ static control_t cfstore_create_test_00(const size_t call_count)
272273
}
273274

274275

275-
/** @brief
276+
/** @brief Test case to change the value blob size of pre-existing key.
276277
*
277-
* Test case to change the value blob size of pre-existing key.
278278
* The test does the following:
279279
* - creates a cfstore with ~10 entries.
280-
* - for a mid-cfstore entry, grow the value blob size
280+
* - for a mid-cfstore entry, double the value blob size.
281281
* - check all the cfstore entries can be read correctly and their
282282
* data agrees with the data supplied upon creation.
283-
* - grow the mid-entry value blob size to be ~double the initial size.
283+
* - shrink the mid-entry value blob size to be ~half the initial size.
284284
* - check all the cfstore entries can be read correctly and their
285285
* data agrees with the data supplied upon creation.
286286
*
@@ -501,14 +501,13 @@ control_t cfstore_create_test_04_end(const size_t call_count)
501501
return CaseNext;
502502
}
503503

504-
/**@brief
505-
*
506-
* Test to create ~500 kvs. The amount of data store in the cfstore is as follows:
507-
* - total = (220*500)+(256/64)*500*501/2 500*8 = 8236000 = 8.236M
504+
505+
/**
506+
* @brief Support function for test cases 05
508507
*
509-
* @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
508+
* Create enough KV's to consume the whole of available memory
510509
*/
511-
control_t cfstore_create_test_05_end(const size_t call_count)
510+
int32_t cfstore_create_test_05_core(const size_t call_count, uint32_t* bytes_stored_ex)
512511
{
513512
int32_t ret = ARM_DRIVER_ERROR;
514513
uint32_t i = 0;
@@ -523,7 +522,10 @@ control_t cfstore_create_test_05_end(const size_t call_count)
523522
ARM_CFSTORE_DRIVER* drv = &cfstore_driver;
524523

525524
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
526-
(void) call_count;
525+
526+
/* Initialize() */
527+
cfstore_utest_default_start(call_count);
528+
527529
value_buf = (char*) malloc(max_value_buf_size);
528530
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: out of memory.\n", __func__);
529531
TEST_ASSERT_MESSAGE(value_buf != NULL, cfstore_create_utest_msg_g);
@@ -549,6 +551,48 @@ control_t cfstore_create_test_05_end(const size_t call_count)
549551
free(value_buf);
550552
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__);
551553
TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
554+
if(bytes_stored_ex){
555+
*bytes_stored_ex = bytes_stored;
556+
}
557+
return ret;
558+
}
559+
560+
561+
/**
562+
* @brief Test case to check cfstore recovers from out of memory
563+
* errors without leaking memory
564+
*
565+
* This test case does the following:
566+
* 1. Start loop.
567+
* 2. Initializes CFSTORE.
568+
* 3. Creates as many KVs as required to run out of memory. The number of bytes B
569+
* allocated before running out of memory is recorded.
570+
* 4. For loop i, check that B_i = B_i-1 for i>0 i.e. that no memory has been leaked
571+
* 5. Uninitialize(), which should clean up any cfstore internal state including
572+
* freeing the internal memeory area.
573+
* 6. Repeat from step 1 N times.
574+
*/
575+
control_t cfstore_create_test_05(const size_t call_count)
576+
{
577+
uint32_t i = 0;
578+
int32_t ret = ARM_DRIVER_ERROR;
579+
uint32_t bytes_stored = 0;
580+
uint32_t bytes_stored_prev = 0;
581+
const uint32_t max_loops = 50;
582+
583+
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
584+
for(i = 0; i < max_loops; i++) {
585+
ret = cfstore_create_test_05_core(call_count, &bytes_stored);
586+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: cfstore_create_test_05_core() failed (ret = %d.\n", __func__, (int) ret);
587+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
588+
589+
if(i > 1) {
590+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: memory leak: stored %d bytes on loop %d, but %d bytes on loop %d .\n", __func__, (int) bytes_stored, (int) i, (int) bytes_stored_prev, (int) i-1);
591+
TEST_ASSERT_MESSAGE(bytes_stored == bytes_stored_prev, cfstore_create_utest_msg_g);
592+
593+
}
594+
bytes_stored_prev = bytes_stored;
595+
}
552596
return CaseNext;
553597
}
554598

@@ -580,9 +624,8 @@ cfstore_create_key_name_validate_t cfstore_create_test_06_data[] = {
580624
/// @endcond
581625

582626

583-
/**@brief
584-
*
585-
* Test whether a key name can be created or not.
627+
/**
628+
* @brief Test whether a key name can be created or not.
586629
*
587630
* @param key_name
588631
* name of the key to create in the store
@@ -663,6 +706,109 @@ control_t cfstore_create_test_06_end(const size_t call_count)
663706
return CaseNext;
664707
}
665708

709+
710+
/** @brief Test case to change the value blob size of pre-existing
711+
* key in a way that causes the memory area to realloc-ed,
712+
*
713+
* The test is a modified version of cfstore_create_test_01_end which
714+
* - creates KVs,
715+
* - mallocs a memory object on the heap
716+
* - increases the size of one of the existing KVs, causing the
717+
* internal memory area to be realloc-ed.
718+
*
719+
* In detail, the test does the following:
720+
* 1. creates a cfstore with ~10 entries. This causes the configuration
721+
* store to realloc() heap memory for KV storage.
722+
* 2. mallocs a memory object on heap.
723+
* 3. for a mid-cfstore entry, double the value blob size. This will cause the
724+
* cfstore memory area to be realloced.
725+
* 4. check all the cfstore entries can be read correctly and their
726+
* data agrees with the data supplied upon creation.
727+
* 5. shrink the mid-entry value blob size to be ~half the initial size.
728+
* check all the cfstore entries can be read correctly and their
729+
* data agrees with the data supplied upon creation.
730+
*
731+
* @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
732+
*/
733+
control_t cfstore_create_test_07_end(const size_t call_count)
734+
{
735+
int32_t ret = ARM_DRIVER_ERROR;
736+
void *test_buf1 = NULL;
737+
ARM_CFSTORE_FMODE flags;
738+
cfstore_kv_data_t* node = NULL;
739+
ARM_CFSTORE_DRIVER* drv = &cfstore_driver;
740+
741+
CFSTORE_FENTRYLOG("%s:entered\n", __func__);
742+
(void) call_count;
743+
memset(&flags, 0, sizeof(flags));
744+
745+
/* step 1 */
746+
ret = cfstore_test_create_table(cfstore_create_test_01_data_step_01);
747+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to add cfstore_create_test_01_data_head (ret=%d).\n", __func__, (int) ret);
748+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
749+
750+
/* step 2 */
751+
test_buf1 = malloc(CFSTORE_CREATE_MALLOC_SIZE);
752+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocate memory (test_buf1=%p)\n", __func__, test_buf1);
753+
TEST_ASSERT_MESSAGE(test_buf1 != NULL, cfstore_create_utest_msg_g);
754+
755+
/* step 3. find cfstore_create_test_01_data[0] and grow the KV MID_ENTRY_01 to MID_ENTRY_02 */
756+
ret = cfstore_create_test_KV_change(&cfstore_create_test_01_data[0], &cfstore_create_test_01_data[1]);
757+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to increase size of KV (ret=%d).\n", __func__, (int) ret);
758+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
759+
760+
/* step 4. Now check that the KVs are all present and correct */
761+
node = cfstore_create_test_01_data_step_02;
762+
while(node->key_name != NULL)
763+
{
764+
ret = cfstore_test_check_node_correct(node);
765+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value);
766+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
767+
node++;
768+
}
769+
/* revert CFSTORE_LOG for more trace */
770+
CFSTORE_DBGLOG("KV successfully increased in size and other KVs remained unchanged.%s", "\n");
771+
772+
/* Shrink the KV from KV MID_ENTRY_02 to MID_ENTRY_03 */
773+
ret = cfstore_create_test_KV_change(&cfstore_create_test_01_data[1], &cfstore_create_test_01_data[2]);
774+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to decrease size of KV (ret=%d).\n", __func__, (int) ret);
775+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
776+
777+
/* Step 5. Now check that the KVs are all present and correct */
778+
node = cfstore_create_test_01_data_step_03;
779+
while(node->key_name != NULL)
780+
{
781+
ret = cfstore_test_check_node_correct(node);
782+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value);
783+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
784+
node++;
785+
}
786+
/* revert CFSTORE_LOG for more trace */
787+
CFSTORE_DBGLOG("KV successfully decreased in size and other KVs remained unchanged.%s", "\n");
788+
789+
/* Delete the KV */
790+
ret = cfstore_test_delete(cfstore_create_test_01_data[2].key_name);
791+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:failed to delete node(key_name=\"%s\")\n", __func__, node->key_name);
792+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
793+
794+
/* Now check that the KVs are all present and correct */
795+
node = cfstore_create_test_01_data_step_04;
796+
while(node->key_name != NULL)
797+
{
798+
ret = cfstore_test_check_node_correct(node);
799+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value);
800+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
801+
node++;
802+
}
803+
804+
free(test_buf1);
805+
ret = drv->Uninitialize();
806+
CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__);
807+
TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g);
808+
return CaseNext;
809+
}
810+
811+
666812
/// @cond CFSTORE_DOXYGEN_DISABLE
667813
utest::v1::status_t greentea_setup(const size_t number_of_cases)
668814
{
@@ -682,10 +828,11 @@ Case cases[] = {
682828
Case("CREATE_test_03_end", cfstore_create_test_03_end),
683829
Case("CREATE_test_04_start", cfstore_utest_default_start),
684830
Case("CREATE_test_04_end", cfstore_create_test_04_end),
685-
Case("CREATE_test_05_start", cfstore_utest_default_start),
686-
Case("CREATE_test_05_end", cfstore_create_test_05_end),
831+
Case("CREATE_test_05", cfstore_create_test_05),
687832
Case("CREATE_test_06_start", cfstore_utest_default_start),
688833
Case("CREATE_test_06_end", cfstore_create_test_06_end),
834+
Case("CREATE_test_07_start", cfstore_utest_default_start),
835+
Case("CREATE_test_07_end", cfstore_create_test_07_end),
689836
};
690837

691838

0 commit comments

Comments
 (0)