2011-02-12 00:22:29 -05:00
|
|
|
#include <stdlib.h>
|
2013-07-04 23:58:16 -04:00
|
|
|
#include <stdint.h>
|
2011-02-12 00:22:29 -05:00
|
|
|
#include <errno.h>
|
2013-07-04 23:58:16 -04:00
|
|
|
#include "libc.h"
|
2011-02-12 00:22:29 -05:00
|
|
|
|
allow interposition/replacement of allocator (malloc)
replacement is subject to conditions on the replacement functions.
they may only call functions which are async-signal-safe, as specified
either by POSIX or as an implementation-defined extension. if any
allocator functions are replaced, at least malloc, realloc, and free
must be provided. if calloc is not provided, it will behave as
malloc+memset. any of the memalign-family functions not provided will
fail with ENOMEM.
in order to implement the above properties, calloc and __memalign
check that they are using their own malloc or free, respectively.
choice to check malloc or free is based on considerations of
supporting __simple_malloc. in order to make this work, calloc is
split into separate versions for __simple_malloc and full malloc;
commit ba819787ee93ceae94efd274f7849e317c1bff58 already did most of
the split anyway, and completing it saves an extra call frame.
previously, use of -Bsymbolic-functions made dynamic interposition
impossible. now, we are using an explicit dynamic-list, so add
allocator functions to the list. most are not referenced anyway, but
all are added for completeness.
2018-04-17 18:36:19 -04:00
|
|
|
void __internal_free(void *);
|
2013-07-04 23:58:16 -04:00
|
|
|
|
|
|
|
void *__memalign(size_t align, size_t len)
|
2011-02-12 00:22:29 -05:00
|
|
|
{
|
2013-07-04 23:58:16 -04:00
|
|
|
unsigned char *mem, *new, *end;
|
|
|
|
size_t header, footer;
|
|
|
|
|
|
|
|
if ((align & -align) != align) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
allow interposition/replacement of allocator (malloc)
replacement is subject to conditions on the replacement functions.
they may only call functions which are async-signal-safe, as specified
either by POSIX or as an implementation-defined extension. if any
allocator functions are replaced, at least malloc, realloc, and free
must be provided. if calloc is not provided, it will behave as
malloc+memset. any of the memalign-family functions not provided will
fail with ENOMEM.
in order to implement the above properties, calloc and __memalign
check that they are using their own malloc or free, respectively.
choice to check malloc or free is based on considerations of
supporting __simple_malloc. in order to make this work, calloc is
split into separate versions for __simple_malloc and full malloc;
commit ba819787ee93ceae94efd274f7849e317c1bff58 already did most of
the split anyway, and completing it saves an extra call frame.
previously, use of -Bsymbolic-functions made dynamic interposition
impossible. now, we are using an explicit dynamic-list, so add
allocator functions to the list. most are not referenced anyway, but
all are added for completeness.
2018-04-17 18:36:19 -04:00
|
|
|
if (len > SIZE_MAX - align || free != __internal_free) {
|
2013-07-04 23:58:16 -04:00
|
|
|
errno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (align <= 4*sizeof(size_t)) {
|
|
|
|
if (!(mem = malloc(len)))
|
|
|
|
return NULL;
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(mem = malloc(len + align-1)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
new = (void *)((uintptr_t)mem + align-1 & -align);
|
fix heap corruption bug in memalign
this bug was caught by the new footer-corruption check in realloc and
free.
if the block returned by malloc was already aligned to the desired
alignment, memalign's logic to split off the misaligned head was
incorrect; rather than writing to a point inside the allocated block,
it was overwriting the footer of the previous block on the heap with
the value 1 (length 0 plus an in-use flag).
fortunately, the impact of this bug was fairly low. (this is probably
why it was not caught sooner.) due to the way the heap works, malloc
will never return a block whose previous block is free. (doing so would
be harmful because it would increase fragmentation with no benefit.)
the footer is actually not needed for in-use blocks, except that its
in-use bit needs to remain set so that it does not get merged with
free blocks, so there was no harm in it being set to 1 instead of the
correct value.
however, there is one case where this bug could have had an impact: in
multi-threaded programs, if another thread freed the previous block
after memalign's call to malloc returned, but before memalign
overwrote the previous block's footer, the resulting block in the free
list could be left in a corrupt state. I have not analyzed the impact
of this bad state and whether it could lead to more serious
malfunction.
2013-07-23 23:18:49 -04:00
|
|
|
if (new == mem) return mem;
|
|
|
|
|
|
|
|
header = ((size_t *)mem)[-1];
|
2013-07-04 23:58:16 -04:00
|
|
|
|
|
|
|
if (!(header & 7)) {
|
|
|
|
((size_t *)new)[-2] = ((size_t *)mem)[-2] + (new-mem);
|
|
|
|
((size_t *)new)[-1] = ((size_t *)mem)[-1] - (new-mem);
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = mem + (header & -8);
|
|
|
|
footer = ((size_t *)end)[-2];
|
|
|
|
|
|
|
|
((size_t *)mem)[-1] = header&7 | new-mem;
|
|
|
|
((size_t *)new)[-2] = footer&7 | new-mem;
|
|
|
|
((size_t *)new)[-1] = header&7 | end-new;
|
|
|
|
((size_t *)end)[-2] = footer&7 | end-new;
|
|
|
|
|
2013-07-23 23:40:26 -04:00
|
|
|
free(mem);
|
2013-07-04 23:58:16 -04:00
|
|
|
return new;
|
2011-02-12 00:22:29 -05:00
|
|
|
}
|
2013-07-04 23:58:16 -04:00
|
|
|
|
|
|
|
weak_alias(__memalign, memalign);
|