eina/mp/one_big: fix alignment issues.

When over-allocating (past "pool->max" items) a memory slice will be
allocated to the new item as a linked list using Eina_Inlist.

The original code was placing the Eina_Inlist structure (3 pointers)
at the beginning of the allocated memory. However the item must have
proper alignment based on "pool->item_size", otherwise a structure may
end with unaligned members. Take for example MIPS 32 bits, it uses 4
bytes pointers with 8 bytes double. A structure containing a double
could have it unaligned as 12 % 8 = 4 (12 is the size of Eina_Inlist,
that contains 3 pointers), and MIPS doesn't allow unaligned access.

Albin Tonnerre (Lutin) spotted this in his Debian MIPS test machine,
it was breaking at eet_data_get_double() that was storing an unaligned
double. This was being called from within edje test suite.

The current code will place the list node after the requested
"pool->item_size", of course guaranteeing the pointer inside the node
is aligned (otherwise a "char" or "short" would break its alignment).
This commit is contained in:
Gustavo Sverzut Barbieri 2014-01-20 21:27:58 -02:00
parent edc548fac5
commit dbc4669d68
1 changed files with 23 additions and 8 deletions

View File

@ -49,6 +49,12 @@
#endif
#define WRN(...) EINA_LOG_DOM_WARN(_eina_one_big_mp_log_dom, __VA_ARGS__)
#define OVER_MEM_TO_LIST(_pool, _over_mem) \
((Eina_Inlist *)(((char *)_over_mem) + (_pool)->offset_to_item_inlist))
#define OVER_MEM_FROM_LIST(_pool, _node) \
((void *)(((char *)_node) - (_pool)->offset_to_item_inlist))
static int _eina_one_big_mp_log_dom = -1;
typedef struct _One_Big One_Big;
@ -57,6 +63,7 @@ struct _One_Big
const char *name;
int item_size;
int offset_to_item_inlist;
int usage;
int over;
@ -114,15 +121,14 @@ eina_one_big_malloc(void *data, EINA_UNUSED unsigned int size)
}
retry_smaller:
mem = malloc(sizeof(Eina_Inlist) + pool->item_size);
mem = malloc(sizeof(Eina_Inlist) + pool->offset_to_item_inlist);
if (mem)
{
Eina_Inlist *node = OVER_MEM_TO_LIST(pool, mem);
pool->over++;
/* Only need to zero list elements and not the payload here */
memset(mem, 0, sizeof(Eina_Inlist));
pool->over_list = eina_inlist_append(pool->over_list,
(Eina_Inlist *)mem);
mem = ((unsigned char *)mem) + sizeof(Eina_Inlist);
memset(node, 0, sizeof(Eina_Inlist));
pool->over_list = eina_inlist_append(pool->over_list, node);
}
#ifndef NVALGRIND
VALGRIND_MAKE_MEM_NOACCESS(mem, pool->item_size);
@ -162,7 +168,7 @@ eina_one_big_free(void *data, void *ptr)
#endif
Eina_Inlist *il;
il = (Eina_Inlist *)(((unsigned char *)ptr) - sizeof(Eina_Inlist));
il = OVER_MEM_TO_LIST(pool, ptr);
#ifndef NDEBUG
for (it = pool->over_list; it != NULL; it = it->next)
@ -172,7 +178,7 @@ eina_one_big_free(void *data, void *ptr)
#endif
pool->over_list = eina_inlist_remove(pool->over_list, il);
free(il);
free(ptr);
pool->over--;
}
@ -211,6 +217,14 @@ eina_one_big_init(const char *context,
pool->item_size = eina_mempool_alignof(item_size);
pool->max = va_arg(args, int);
pool->offset_to_item_inlist = pool->item_size;
if (pool->offset_to_item_inlist % (int)sizeof(void *) != 0)
{
pool->offset_to_item_inlist =
(((pool->offset_to_item_inlist / (int)sizeof(void *)) + 1) *
(int)sizeof(void *));
}
if (length)
{
pool->name = (const char *)(pool + 1);
@ -253,8 +267,9 @@ eina_one_big_shutdown(void *data)
while (pool->over_list)
{
Eina_Inlist *il = pool->over_list;
void *ptr = OVER_MEM_FROM_LIST(pool, il);
pool->over_list = eina_inlist_remove(pool->over_list, il);
free(il);
free(ptr);
pool->over--;
}
}