refactor(array): make lv_array_copy more robust (#10446)
Arduino Lint / lint (push) Canceled after 0s
Build Examples with C++ Compiler / build-examples (push) Canceled after 0s
MicroPython CI / Build esp32 port (push) Canceled after 0s
MicroPython CI / Build rp2 port (push) Canceled after 0s
MicroPython CI / Build stm32 port (push) Canceled after 0s
MicroPython CI / Build unix port (push) Canceled after 0s
C/C++ CI / Build OPTIONS_LINUX_OPENGLES - Ubuntu (push) Canceled after 0s
C/C++ CI / Build OPTIONS_LINUX_SW - Ubuntu (push) Canceled after 0s
C/C++ CI / Build OPTIONS_MINIMAL - Ubuntu (push) Canceled after 0s
C/C++ CI / Build OPTIONS_NUTTX - Ubuntu (push) Canceled after 0s
C/C++ CI / Build OPTIONS_MINIMAL - cl - Windows (push) Canceled after 0s
C/C++ CI / Build OPTIONS_MINIMAL - gcc - Windows (push) Canceled after 0s
C/C++ CI / Build OPTIONS_WINDOWS_SW - cl - Windows (push) Canceled after 0s
C/C++ CI / Build OPTIONS_WINDOWS_SW - gcc - Windows (push) Canceled after 0s
C/C++ CI / Build ESP IDF ESP32S3 (push) Canceled after 0s
C/C++ CI / Run tests with 32bit build (push) Canceled after 0s
C/C++ CI / Run tests with 64bit build (push) Canceled after 0s
BOM Check / bom-check (push) Canceled after 0s
Verify GDB constants are up-to-date / verify-gdb-consts (push) Canceled after 0s
Verify the widget property name / verify-property-name (push) Canceled after 0s
Verify code formatting / verify-formatting (push) Canceled after 0s
Compare file templates with file names / template-check (push) Canceled after 0s
Code Generation / Code Generation (push) Canceled after 0s
Build Docs / build-and-deploy (push) Canceled after 0s
Build .deb packages / build (push) Canceled after 0s
Validate pkg-config and CMake config / cmake (linux) (push) Canceled after 0s
Validate pkg-config and CMake config / pkgconfig (linux) (push) Canceled after 0s
Validate pkg-config and CMake config / cmake (linux-3d) (push) Canceled after 0s
Validate pkg-config and CMake config / pkgconfig (linux-3d) (push) Canceled after 0s
Test API JSON generator / Test API JSON (push) Canceled after 0s
Install LVGL using CMake / build-examples (private) (push) Canceled after 0s
Install LVGL using CMake / build-examples (public) (push) Canceled after 0s
Check Makefile / Build using Makefile (push) Canceled after 0s
Check Makefile for UEFI / Build using Makefile for UEFI (push) Canceled after 0s
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/benchmark_results_comment/test.sh) (push) Canceled after 0s
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/filter_docker_logs/test.sh) (push) Canceled after 0s
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/serialize_results/test.sh) (push) Canceled after 0s
Emulated Performance Test / ARM Emulated Benchmark 32b - lv_conf_perf32b (push) Canceled after 0s
Emulated Performance Test / ARM Emulated Benchmark 64b - lv_conf_perf64b (push) Canceled after 0s
Emulated Performance Test / ARM Emulated Benchmark - Save PR Number (push) Canceled after 0s

This commit is contained in:
KartiK B Kenchi
2026-08-18 11:14:29 +02:00
committed by GitHub
parent f62f5e8769
commit 326c353b0a
3 changed files with 178 additions and 3 deletions
+40 -2
View File
@@ -68,11 +68,49 @@ void lv_array_deinit(lv_array_t * array)
void lv_array_copy(lv_array_t * target, const lv_array_t * source)
{
LV_ASSERT_NULL(target);
LV_ASSERT_NULL(source);
if(target == source) return;
lv_array_deinit(target);
/* Guard against multiplication overflow during allocation */
if(source->capacity > 0 && source->element_size > 0) {
if(source->capacity > UINT32_MAX / source->element_size) {
lv_memzero(target, sizeof(lv_array_t));
return;
}
void * data = lv_malloc(source->capacity * source->element_size);
if(data == NULL) {
lv_memzero(target, sizeof(lv_array_t));
return;
}
target->data = data;
target->capacity = source->capacity;
target->element_size = source->element_size;
target->inner_alloc = true;
target->size = 0;
}
else {
void * data = lv_malloc(0);
if(data == NULL) {
lv_memzero(target, sizeof(lv_array_t));
return;
}
target->data = data;
target->capacity = source->capacity;
target->element_size = source->element_size;
target->inner_alloc = true;
target->size = 0;
}
if(lv_array_is_empty(source)) {
return;
}
lv_array_deinit(target);
lv_array_init(target, source->capacity, source->element_size);
lv_memcpy(target->data, source->data, source->size * source->element_size);
target->size = source->size;
}
+3 -1
View File
@@ -119,7 +119,9 @@ static inline bool lv_array_is_full(const lv_array_t * array)
/**
* Copy an array to another.
* @note this will create a new array with the same capacity and size as the source array.
* @note target is always made an exact copy of source. Any existing content in target is
* deinitialized. If source is empty, target will also be empty. If target's previous
* contents need to be preserved when source is empty, check `lv_array_is_empty` first.
* @param target pointer to an `lv_array_t` variable to copy to
* @param source pointer to an `lv_array_t` variable to copy from
*/
+135
View File
@@ -305,4 +305,139 @@ void test_array_assign(void)
TEST_ASSERT_EQUAL(LV_RESULT_INVALID, lv_array_assign(&array, 5, &v));
}
void test_array_copy_empty_to_uninitialized(void)
{
uint32_t mem_before = lv_test_get_free_mem();
lv_array_t target = {0};
lv_array_t source;
lv_array_init(&source, 2, sizeof(int32_t));
lv_array_copy(&target, &source);
TEST_ASSERT_EQUAL_UINT32(0, lv_array_size(&target));
TEST_ASSERT_EQUAL_UINT32(2, lv_array_capacity(&target));
TEST_ASSERT_NOT_NULL(target.data);
lv_array_deinit(&target);
lv_array_deinit(&source);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
}
void test_array_copy_empty_to_populated(void)
{
uint32_t mem_before = lv_test_get_free_mem();
lv_array_t target;
lv_array_init(&target, 4, sizeof(int32_t));
int32_t val = 42;
lv_array_push_back(&target, &val);
lv_array_t source;
lv_array_init(&source, 2, sizeof(int32_t));
lv_array_copy(&target, &source);
/* Verify old target memory was freed and new empty capacity was allocated */
TEST_ASSERT_EQUAL_UINT32(0, lv_array_size(&target));
TEST_ASSERT_EQUAL_UINT32(2, lv_array_capacity(&target));
TEST_ASSERT_NOT_NULL(target.data);
lv_array_deinit(&target);
lv_array_deinit(&source);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
}
void test_array_copy_zero_capacity(void)
{
uint32_t mem_before = lv_test_get_free_mem();
lv_array_t target = {0};
lv_array_t source;
lv_array_init(&source, 0, 0);
lv_array_copy(&target, &source);
TEST_ASSERT_EQUAL_UINT32(0, lv_array_size(&target));
TEST_ASSERT_EQUAL_UINT32(0, lv_array_capacity(&target));
TEST_ASSERT_NOT_NULL(target.data);
lv_array_deinit(&target);
lv_array_deinit(&source);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
}
void test_array_copy_allocation_failure(void)
{
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN && !LV_USE_ASSERT_MALLOC
uint32_t mem_before = lv_test_get_free_mem();
lv_array_t target;
lv_array_init(&target, 2, sizeof(int32_t));
int32_t val = 42;
lv_array_push_back(&target, &val);
lv_array_t source;
uint8_t dummy_buf[4];
lv_array_init_from_buf(&source, dummy_buf, LV_MEM_SIZE + 1, 1);
/* Copying source with huge capacity should cause allocation failure in init */
lv_array_copy(&target, &source);
/* Verify target was zeroed/safe-to-deinit, and no memory was leaked */
TEST_ASSERT_NULL(target.data);
TEST_ASSERT_EQUAL_UINT32(0, lv_array_size(&target));
TEST_ASSERT_EQUAL_UINT32(0, lv_array_capacity(&target));
lv_array_deinit(&target);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
#endif
}
void test_array_copy_overflow(void)
{
uint32_t mem_before = lv_test_get_free_mem();
lv_array_t target;
lv_array_init(&target, 2, sizeof(int32_t));
int32_t val = 42;
lv_array_push_back(&target, &val);
lv_array_t source;
uint8_t dummy_buf[4];
/* Initialize source with capacity and element_size that overflows 32-bit multiplication */
lv_array_init_from_buf(&source, dummy_buf, UINT32_MAX / 2 + 2, 2);
/* Copying should overflow and be handled gracefully (zeroed out) */
lv_array_copy(&target, &source);
TEST_ASSERT_NULL(target.data);
TEST_ASSERT_EQUAL_UINT32(0, lv_array_size(&target));
TEST_ASSERT_EQUAL_UINT32(0, lv_array_capacity(&target));
lv_array_deinit(&target);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
}
void test_array_copy_self(void)
{
uint32_t mem_before = lv_test_get_free_mem();
lv_array_t target;
lv_array_init(&target, 4, sizeof(int32_t));
int32_t val = 42;
lv_array_push_back(&target, &val);
/* Copying to self should be a no-op */
lv_array_copy(&target, &target);
TEST_ASSERT_EQUAL_UINT32(1, lv_array_size(&target));
TEST_ASSERT_EQUAL_UINT32(4, lv_array_capacity(&target));
int32_t * r = lv_array_at(&target, 0);
TEST_ASSERT_EQUAL_INT32(42, *r);
lv_array_deinit(&target);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 0);
}
#endif