eina/mp/one_big: fix alignment issues.
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Mon, 20 Jan 2014 23:27:58 +0000 (21:27 -0200)
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Mon, 20 Jan 2014 23:44:42 +0000 (21:44 -0200)
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).

src/modules/eina/mp/one_big/eina_one_big.c

index 470a2ddf056e284ae71203f609be08d634376b5e..e384e52632f80ff7f3d4aad998d6cd0b73159a79 100644 (file)
 #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--;
           }
      }