mirror of
https://github.com/lvgl/lvgl.git
synced 2026-09-25 08:34:06 +08:00
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
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:
+40
-2
@@ -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
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user