fix(cache): align node size to avoid unaligned access runtime errors (#9913)

This commit is contained in:
André Costa
2026-03-29 18:26:58 +02:00
committed by GitHub
parent eb01918469
commit 5b1b13c761
2 changed files with 52 additions and 7 deletions
+12 -7
View File
@@ -10,11 +10,14 @@
#include "../../stdlib/lv_sprintf.h"
#include "../lv_assert.h"
#include "lv_cache_entry_private.h"
#include "../../misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_CACHE_ENTRY_ALIGN_NODE_SIZE(size) (LV_ALIGN_UP(size, sizeof(void *)))
/**********************
* TYPEDEFS
**********************/
@@ -87,7 +90,8 @@ bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry)
void * lv_cache_entry_get_data(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return (uint8_t *)entry - entry->node_size;
return (uint8_t *)entry - LV_CACHE_ENTRY_ALIGN_NODE_SIZE(entry->node_size);
}
void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry)
@@ -114,7 +118,7 @@ void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data)
lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size)
{
LV_ASSERT_NULL(data);
return (lv_cache_entry_t *)((uint8_t *)data + node_size);
return (lv_cache_entry_t *)((uint8_t *)data + LV_CACHE_ENTRY_ALIGN_NODE_SIZE(node_size));
}
void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache)
@@ -131,23 +135,24 @@ const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry)
uint32_t lv_cache_entry_get_size(const uint32_t node_size)
{
return node_size + sizeof(lv_cache_entry_t);
return LV_CACHE_ENTRY_ALIGN_NODE_SIZE(node_size) + sizeof(lv_cache_entry_t);
}
lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache)
lv_cache_entry_t * lv_cache_entry_alloc(uint32_t node_size, const lv_cache_t * cache)
{
/* Align node_size so that accessing the entry data afterwards is done on an aligned byte address */
void * res = lv_malloc_zeroed(lv_cache_entry_get_size(node_size));
LV_ASSERT_MALLOC(res)
if(res == NULL) {
if(!res) {
LV_LOG_ERROR("malloc failed");
return NULL;
}
lv_cache_entry_t * entry = (lv_cache_entry_t *)((uint8_t *)res + node_size);
lv_cache_entry_t * entry = (lv_cache_entry_t *)((uint8_t *)res + LV_CACHE_ENTRY_ALIGN_NODE_SIZE(node_size));
lv_cache_entry_init(entry, cache, node_size);
return entry;
}
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size)
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, uint32_t node_size)
{
LV_ASSERT_NULL(entry);
LV_ASSERT_NULL(cache);
+40
View File
@@ -320,5 +320,45 @@ void test_cache_entry_alloc(void)
lv_cache_entry_delete(entry);
lv_cache_destroy(cache, NULL);
}
void test_cache_entry_alloc_unaligned_node_size(void)
{
const uint32_t unaligned_sizes[] = { 3, 13 };
for(size_t s = 0; s < sizeof(unaligned_sizes) / sizeof(unaligned_sizes[0]); ++s) {
uint32_t node_size = unaligned_sizes[s];
lv_cache_t * cache = create_cache(&lv_cache_class_lru_rb_size, CACHE_SIZE_BYTES);
TEST_ASSERT_NOT_NULL(cache);
lv_cache_entry_t * entry = lv_cache_entry_alloc(node_size, cache);
TEST_ASSERT_NOT_NULL(entry);
TEST_ASSERT_EQUAL_MESSAGE(
0, ((uintptr_t)entry) % sizeof(void *),
"lv_cache_entry_t * must be pointer-aligned regardless of node_size");
void * data = lv_cache_entry_get_data(entry);
TEST_ASSERT_NOT_NULL(data);
TEST_ASSERT_EQUAL_MESSAGE(
0, ((uintptr_t)data) % sizeof(void *),
"lv_cache_entry_get_data() must be pointer-aligned regardless of node_size");
lv_cache_entry_t * roundtripped = lv_cache_entry_get_entry(data, node_size);
TEST_ASSERT_EQUAL_PTR_MESSAGE(
entry, roundtripped,
"lv_cache_entry_get_entry(lv_cache_entry_get_data(e), node_size) "
"must recover the original entry pointer");
TEST_ASSERT_EQUAL_UINT32_MESSAGE(
node_size, lv_cache_entry_get_node_size(entry),
"get_node_size() must return the original unaligned node_size");
TEST_ASSERT_TRUE_MESSAGE(
(uintptr_t)data < (uintptr_t)entry,
"data pointer must lie before the entry header in the allocation block");
lv_cache_entry_delete(entry);
lv_cache_destroy(cache, NULL);
}
}
#endif