[cairo] fix alignment issue on SPARC processors
Bryce Harrington
bryce at osg.samsung.com
Sat Dec 6 00:52:07 PST 2014
On Fri, Dec 05, 2014 at 02:59:06PM +0100, Uli Schlachter wrote:
> Hi Nicolas,
>
> Am 04.12.2014 um 20:44 schrieb Nicolas Setton:
> [...]
> >> I would instead suggest to add a new member "int64_t data" to the struct with a
> >> suitable comment explaining why it is of type int64_t. Instead of using
> >> ((unsigned char *)chunk + sizeof(*chunk)) for calculating the beginning of the
> >> data area of the chunk, the code could then use (unsigned char*)&chunk->data
> >> which IMO is more readable anyway.
> >
> > I didn't understand this suggestion.
>
> Could you test the attached patch? Also, this is pretty much the idea that I had
> in mind. Compared to your patch, it does not add a hole to struct _pool_chunk on
> 64bit platforms (in fact, nothing should change at all on 64bit).
>
> I basically did no testing at all on this. The test suite run on the image
> backend does not crash. So it would also be nice if others could take a look at
> this and spot my mistakes. Pointer arithmetic is scary.
>
> Cheers,
> Uli
> --
> "In the beginning the Universe was created. This has made a lot of
> people very angry and has been widely regarded as a bad move."
Inline patches -gt attachments. ;-)
> [0;0m>From 9f318fc51963c221faf26b2877804d106a31b3e9 Mon Sep 17 00:00:00 2001[0;0m
> [0;0mFrom: Uli Schlachter <psychon at znc.in>[0;0m
> [0;0mDate: Fri, 5 Dec 2014 14:43:26 +0100[0;0m
> [0;0mSubject: [PATCH] tor-scan-converter: Correctly align 64bit types[0;0m
> [0;0m[0;0m
> [0;0mOn 32bit SPARC the scan converter was causing a SIGBUS due to an unaligned[0;0m
> [0;0mmemory access while accessing an int64_t. This memory accessing was to struct[0;0m
> [0;0mquorem's rem member.[0;0m
> [0;0m[0;0m
> [0;0mThis crash occurred because the rot-scan-converter contains its own[0;0m
s/rot-/tor-/
> [0;0mimplementation of a memory pool. This implementation only guarantees an[0;0m
> [0;0malignment of sizeof(void *), which is less than what a 64 bit type requires on[0;0m
> [0;0m32bit platforms. This 4 byte alignment is guaranteed, because struct _pool_chunk[0;0m
> [0;0m(which is the struct that is used for managing free space) contains elements of[0;0m
> [0;0mthat size and so the size of that struct is a multiple of this size as well.[0;0m
> [0;0m[0;0m
> [0;0mThis problem was introduced with commit 03c3d4b7c15.[0;0m
> [0;0m[0;0m
> [0;0mTo fix this problem, this commit introduces a int64_t member to struct[0;0m
> [0;0m_pool_chunk that marks the beginning of the free data space. Thanks to this, the[0;0m
> [0;0mcompiler ensures proper alignment and sizeof(struct _pool_chunk) becomes a[0;0m
> [0;0mmultiple of 8.[0;0m
> [0;0m[0;0m
> [0;0mHowever, previously the end of the struct marked the beginning of the data and[0;0m
> [0;0msizeof() was used for correctly calculating offsets to the data section. So,[0;0m
> [0;0mjust adding such a member would work, but would also waste some memory. To avoid[0;0m
> [0;0mthis, this commit also changes the rest of the pool implementation to[0;0m
> [0;0maccommodate.[0;0m
> [0;0m[0;0m
> [0;0mReported-by: Nicolas Setton <setton at adacore.com>[0;0m
> [0;0mSigned-off-by: Uli Schlachter <psychon at znc.in>[0;0m
> [0;32m---[0;0m
> [0;0m src/cairo-tor-scan-converter.c | 35 ++++++++++++++++++++---------------[0;0m
> [0;0m 1 file changed, 20 insertions(+), 15 deletions(-)[0;0m
> [0;0m[0;0m
> [0;34mdiff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c[0;0m
> [0;0mindex 14922d0..7adb5ab 100644[0;0m
> [0;32m--- a/src/cairo-tor-scan-converter.c[0;0m
> [1;32m+++ b/src/cairo-tor-scan-converter.c[0;0m
> [1;34m@@ -277,7 +277,8 @@ struct _pool_chunk {[0;0m
> [0;0m * chunk in the pool header. */[0;0m
> [0;0m struct _pool_chunk *prev_chunk;[0;0m
> [0;0m [0;0m
> [0;32m- /* Actual data starts here. Well aligned for pointers. */[0;0m
> [1;32m+ /* Actual data starts here. Well aligned even for 64 bit types. */[0;0m
> [1;32m+ int64_t data;[0;0m
> [0;0m };[0;0m
> [0;0m [0;0m
> [0;0m /* A memory pool. This is supposed to be embedded on the stack or[0;0m
> [1;34m@@ -299,8 +300,11 @@ struct pool {[0;0m
> [0;0m [0;0m
> [0;0m /* Header for the sentinel chunk. Directly following the pool[0;0m
> [0;0m * struct should be some space for embedded elements from which[0;0m
> [0;32m- * the sentinel chunk allocates from. */[0;0m
> [0;32m- struct _pool_chunk sentinel[1];[0;0m
> [1;32m+ * the sentinel chunk allocates from. This is expressed as a char[0;0m
> [1;32m+ * array so that the 'int64_t data' member of _pool_chunk isn't[0;0m
> [1;32m+ * included. This way embedding struct pool in other structs works[0;0m
> [1;32m+ * without wasting space. */[0;0m
> [1;32m+ char sentinel[sizeof(struct _pool_chunk) - sizeof(int64_t)];[0;0m
> [0;0m };[0;0m
> [0;0m [0;0m
> [0;0m /* A polygon edge. */[0;0m
> [1;34m@@ -474,8 +478,9 @@ static struct _pool_chunk *[0;0m
> [0;0m _pool_chunk_create(struct pool *pool, size_t size)[0;0m
> [0;0m {[0;0m
> [0;0m struct _pool_chunk *p;[0;0m
> [1;32m+ size_t sizeof_chunk = sizeof(struct _pool_chunk) - sizeof(int64_t);[0;0m
> [0;0m [0;0m
> [0;32m- p = malloc(size + sizeof(struct _pool_chunk));[0;0m
> [1;32m+ p = malloc(sizeof_chunk + size);[0;0m
Using sizeof_chunk is a nice cleanup, and I'd think the compiler would
optimize it away, although since this same phrase is used in the
sentinel declaration, maybe it's worth making it a preprocessor
definition so it can be used in both places?
> [0;0m if (unlikely (NULL == p))[0;0m
> [0;0m longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));[0;0m
> [0;0m [0;0m
> [1;34m@@ -489,10 +494,10 @@ pool_init(struct pool *pool,[0;0m
> [0;0m size_t embedded_capacity)[0;0m
> [0;0m {[0;0m
> [0;0m pool->jmp = jmp;[0;0m
> [0;32m- pool->current = pool->sentinel;[0;0m
> [1;32m+ pool->current = (void*) &pool->sentinel[0];[0;0m
> [0;0m pool->first_free = NULL;[0;0m
> [0;0m pool->default_capacity = default_capacity;[0;0m
> [0;32m- _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);[0;0m
> [1;32m+ _pool_chunk_init(pool->current, NULL, embedded_capacity);[0;0m
> [0;0m }[0;0m
> [0;0m [0;0m
> [0;0m static void[0;0m
> [1;34m@@ -502,7 +507,7 @@ pool_fini(struct pool *pool)[0;0m
> [0;0m do {[0;0m
> [0;0m while (NULL != p) {[0;0m
> [0;0m struct _pool_chunk *prev = p->prev_chunk;[0;0m
> [0;32m- if (p != pool->sentinel)[0;0m
> [1;32m+ if (p != (void *) &pool->sentinel[0])[0;0m
Is the deref/ref of sentinel just for clarity or is this actually needed
for the fix. (Fine either way, I'm just curious.)
> [0;0m free(p);[0;0m
> [0;0m p = prev;[0;0m
> [0;0m }[0;0m
> [1;34m@@ -542,14 +547,14 @@ _pool_alloc_from_new_chunk([0;0m
> [0;0m chunk = _pool_chunk_create (pool, capacity);[0;0m
> [0;0m pool->current = chunk;[0;0m
> [0;0m [0;0m
> [0;32m- obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);[0;0m
> [1;32m+ obj = ((unsigned char*)&chunk->data + chunk->size);[0;0m
Nice, this makes it easier to grok.
> [0;0m chunk->size += size;[0;0m
> [0;0m return obj;[0;0m
> [0;0m }[0;0m
> [0;0m [0;0m
> [0;0m /* Allocate size bytes from the pool. The first allocated address[0;0m
> [0;32m- * returned from a pool is aligned to sizeof(void*). Subsequent[0;0m
> [0;32m- * addresses will maintain alignment as long as multiples of void* are[0;0m
> [1;32m+ * returned from a pool is aligned to 8 bytes. Subsequent[0;0m
> [1;32m+ * addresses will maintain alignment as long as multiples of 8 are[0;0m
> [0;0m * allocated. Returns the address of a new memory area or %NULL on[0;0m
> [0;0m * allocation failures. The pool retains ownership of the returned[0;0m
> [0;0m * memory. */[0;0m
> [1;34m@@ -559,7 +564,7 @@ pool_alloc (struct pool *pool, size_t size)[0;0m
> [0;0m struct _pool_chunk *chunk = pool->current;[0;0m
> [0;0m [0;0m
> [0;0m if (size <= chunk->capacity - chunk->size) {[0;0m
> [0;32m- void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);[0;0m
> [1;32m+ void *obj = ((unsigned char*)&chunk->data + chunk->size);[0;0m
> [0;0m chunk->size += size;[0;0m
> [0;0m return obj;[0;0m
> [0;0m } else {[0;0m
> [1;34m@@ -573,16 +578,16 @@ pool_reset (struct pool *pool)[0;0m
> [0;0m {[0;0m
> [0;0m /* Transfer all used chunks to the chunk free list. */[0;0m
> [0;0m struct _pool_chunk *chunk = pool->current;[0;0m
> [0;32m- if (chunk != pool->sentinel) {[0;0m
> [0;32m- while (chunk->prev_chunk != pool->sentinel) {[0;0m
> [1;32m+ if (chunk != (void *) &pool->sentinel[0]) {[0;0m
> [1;32m+ while (chunk->prev_chunk != (void *) &pool->sentinel[0]) {[0;0m
> [0;0m chunk = chunk->prev_chunk;[0;0m
> [0;0m }[0;0m
> [0;0m chunk->prev_chunk = pool->first_free;[0;0m
> [0;0m pool->first_free = pool->current;[0;0m
> [0;0m }[0;0m
> [0;0m /* Reset the sentinel as the current chunk. */[0;0m
> [0;32m- pool->current = pool->sentinel;[0;0m
> [0;32m- pool->sentinel->size = 0;[0;0m
> [1;32m+ pool->current = (void *) &pool->sentinel[0];[0;0m
> [1;32m+ pool->current->size = 0;[0;0m
This is intentional to set the current chunk's size to 0?
Assuming it is,
Reviewed-by: Bryce Harrington <bryce at osg.samsung.com>
> [0;0m }[0;0m
> [0;0m [0;0m
> [0;0m /* Rewinds the cell list's cursor to the beginning. After rewinding[0;0m
> [0;32m-- [0;0m
> [0;0m2.1.3[0;0m
> [0;0m[0;0m
More information about the cairo
mailing list