2008-11-12 05:55:19 +01:00
|
|
|
#include "sh.h"
|
|
|
|
|
2008-11-12 08:02:47 +01:00
|
|
|
__RCSID("$MirOS: src/bin/mksh/aalloc.c,v 1.22 2008/11/12 07:02:47 tg Exp $");
|
2008-11-12 05:55:19 +01:00
|
|
|
|
|
|
|
/* mksh integration of aalloc */
|
|
|
|
|
|
|
|
#ifndef AALLOC_ABORT
|
|
|
|
#define AALLOC_ABORT internal_errorf
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef AALLOC_WARN
|
|
|
|
#define AALLOC_WARN internal_warningf
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef AALLOC_RANDOM
|
|
|
|
#if HAVE_ARC4RANDOM
|
|
|
|
#define AALLOC_RANDOM() arc4random()
|
|
|
|
#else
|
|
|
|
#define AALLOC_RANDOM() (rand() * RAND_MAX + rand())
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#define AALLOC_DEBUG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* generic area-based allocator built for mmap malloc or omalloc */
|
|
|
|
|
|
|
|
#define PVALIGN (sizeof (void *))
|
|
|
|
#define PVMASK (sizeof (void *) - 1)
|
|
|
|
|
|
|
|
#ifndef AALLOC_INITSZ
|
2008-11-12 06:46:14 +01:00
|
|
|
#define AALLOC_INITSZ (64 * PVALIGN) /* at least 4 pointers */
|
2008-11-12 05:55:19 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef /* unsigned */ ptrdiff_t TCookie;
|
|
|
|
|
|
|
|
typedef union {
|
|
|
|
TCookie iv;
|
2008-11-12 06:11:05 +01:00
|
|
|
char *pv;
|
2008-11-12 05:55:19 +01:00
|
|
|
} TPtr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The separation between TBlock and TArea does not seem to make
|
|
|
|
* sense at first, especially in the !AALLOC_TRACK case, but is
|
|
|
|
* necessary to keep PArea values constant even if the storage is
|
|
|
|
* enlarged. While we could use an extensible array to keep track
|
|
|
|
* of the TBlock instances, kind of like we use TBlock.storage to
|
|
|
|
* track the allocations, it would require another TBlock member
|
|
|
|
* and a fair amount of backtracking; since omalloc can optimise
|
|
|
|
* pointer sized allocations like a !AALLOC_TRACK TArea, we don't
|
|
|
|
* do that then.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct TBlock {
|
|
|
|
TCookie cookie;
|
2008-11-12 06:11:05 +01:00
|
|
|
char *endp;
|
|
|
|
char *last;
|
2008-11-12 05:55:19 +01:00
|
|
|
void *storage;
|
|
|
|
};
|
|
|
|
typedef struct TBlock *PBlock;
|
|
|
|
|
|
|
|
struct TArea {
|
|
|
|
TPtr bp;
|
|
|
|
#ifdef AALLOC_TRACK
|
|
|
|
TPtr prev;
|
|
|
|
TCookie ocookie;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static TCookie gcookie;
|
2008-11-12 07:10:51 +01:00
|
|
|
#ifdef AALLOC_NO_COOKIES
|
|
|
|
static TCookie fake_cookie;
|
|
|
|
#endif
|
2008-11-12 05:55:19 +01:00
|
|
|
|
|
|
|
#ifdef AALLOC_TRACK
|
|
|
|
static PArea track;
|
|
|
|
static void track_check(void);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef AALLOC_MPROTECT
|
|
|
|
#undef AALLOC_INITSZ
|
|
|
|
#define AALLOC_INITSZ pagesz
|
2008-11-12 06:55:43 +01:00
|
|
|
static long pagesz;
|
2008-11-12 06:32:34 +01:00
|
|
|
#define AALLOC_ALLOW(bp) mprotect((bp), (bp)->endp - (char *)(bp), \
|
|
|
|
PROT_READ | PROT_WRITE)
|
|
|
|
#define AALLOC_DENY(bp) mprotect((bp), (bp)->endp - (char *)(bp), \
|
|
|
|
PROT_NONE)
|
|
|
|
#define AALLOC_PEEK(bp) mprotect((bp), sizeof (struct TArea), \
|
|
|
|
PROT_READ | PROT_WRITE)
|
2008-11-12 05:55:19 +01:00
|
|
|
#else
|
|
|
|
#define AALLOC_ALLOW(bp) /* nothing */
|
|
|
|
#define AALLOC_DENY(bp) /* nothing */
|
|
|
|
#define AALLOC_PEEK(bp) /* nothing */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some nice properties: allocations are always PVALIGNed, which
|
|
|
|
* includes the pointers seen by our user, the forward and back
|
|
|
|
* pointers, the AALLOC_TRACK prev pointer, etc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define safe_realloc(dest, len) do { \
|
|
|
|
if (((dest) = realloc((dest), (len))) == NULL) \
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_ABORT("unable to allocate %lu bytes: %s", \
|
|
|
|
(unsigned long)(len), strerror(errno)); \
|
2008-11-12 05:59:42 +01:00
|
|
|
if ((ptrdiff_t)(dest) & PVMASK) \
|
2008-11-12 05:55:19 +01:00
|
|
|
AALLOC_ABORT("unaligned malloc result: %p", (dest)); \
|
|
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
|
|
|
|
#define MUL_NO_OVERFLOW (1UL << (sizeof (size_t) * 8 / 2))
|
|
|
|
#define safe_muladd(nmemb, size, extra) do { \
|
|
|
|
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && \
|
|
|
|
nmemb > 0 && SIZE_MAX / nmemb < size) \
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_ABORT("attempted integer overflow: %lu * %lu", \
|
|
|
|
(unsigned long)nmemb, (unsigned long)size); \
|
2008-11-12 05:55:19 +01:00
|
|
|
size *= nmemb; \
|
|
|
|
if (size >= SIZE_MAX - extra) \
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_ABORT("unable to allocate %lu bytes: %s", \
|
|
|
|
(unsigned long)size, "value plus extra too big"); \
|
2008-11-12 06:46:45 +01:00
|
|
|
size += extra; \
|
2008-11-12 05:55:19 +01:00
|
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
|
2008-11-12 07:38:08 +01:00
|
|
|
static void adelete_leak(PArea, PBlock, bool, const char *);
|
2008-11-12 05:55:19 +01:00
|
|
|
static PBlock check_bp(PArea, const char *, TCookie);
|
|
|
|
static TPtr *check_ptr(void *, PArea, PBlock *, const char *, const char *);
|
|
|
|
|
|
|
|
PArea
|
|
|
|
anew(void)
|
|
|
|
{
|
|
|
|
PArea ap;
|
|
|
|
PBlock bp;
|
|
|
|
|
2008-11-12 07:05:54 +01:00
|
|
|
#ifdef AALLOC_MPROTECT
|
|
|
|
if (!pagesz) {
|
|
|
|
if ((pagesz = sysconf(_SC_PAGESIZE)) == -1 ||
|
|
|
|
(size_t)pagesz < (size_t)PVALIGN)
|
|
|
|
AALLOC_ABORT("sysconf(_SC_PAGESIZE) failed: %ld %s",
|
|
|
|
pagesz, strerror(errno));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-11-12 05:55:19 +01:00
|
|
|
#ifdef AALLOC_DEBUG
|
|
|
|
if (PVALIGN != 2 && PVALIGN != 4 && PVALIGN != 8 && PVALIGN != 16)
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_ABORT("PVALIGN not a power of two: %lu",
|
|
|
|
(unsigned long)PVALIGN);
|
2008-11-12 05:55:19 +01:00
|
|
|
if (sizeof (TPtr) != sizeof (TCookie) || sizeof (TPtr) != PVALIGN)
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_ABORT("TPtr sizes do not match: %lu, %lu, %lu",
|
|
|
|
(unsigned long)sizeof (TPtr),
|
|
|
|
(unsigned long)sizeof (TCookie), (unsigned long)PVALIGN);
|
2008-11-12 07:05:54 +01:00
|
|
|
if ((size_t)AALLOC_INITSZ < sizeof (struct TBlock))
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_ABORT("AALLOC_INITSZ constant too small: %lu < %lu",
|
|
|
|
(unsigned long)AALLOC_INITSZ,
|
|
|
|
(unsigned long)sizeof (struct TBlock));
|
2008-11-12 05:55:19 +01:00
|
|
|
#endif
|
|
|
|
|
2008-11-12 07:10:51 +01:00
|
|
|
#ifdef AALLOC_NO_COOKIES
|
|
|
|
#define gcookie fake_cookie
|
|
|
|
#endif
|
|
|
|
if (!gcookie) {
|
2008-11-12 05:55:19 +01:00
|
|
|
size_t v;
|
|
|
|
|
|
|
|
/* ensure unaligned cookie */
|
|
|
|
do {
|
2008-11-12 07:10:51 +01:00
|
|
|
gcookie = AALLOC_RANDOM();
|
2008-11-12 05:55:19 +01:00
|
|
|
v = AALLOC_RANDOM() & 7;
|
2008-11-12 07:10:51 +01:00
|
|
|
} while (!(gcookie & PVMASK) || !v);
|
2008-11-12 05:55:19 +01:00
|
|
|
/* randomise seed afterwards */
|
|
|
|
while (v--)
|
|
|
|
AALLOC_RANDOM();
|
|
|
|
#ifdef AALLOC_TRACK
|
|
|
|
atexit(track_check);
|
|
|
|
#endif
|
|
|
|
}
|
2008-11-12 07:10:51 +01:00
|
|
|
#undef gcookie
|
2008-11-12 05:55:19 +01:00
|
|
|
|
|
|
|
ap = NULL; safe_realloc(ap, sizeof (struct TArea));
|
2008-11-12 06:55:43 +01:00
|
|
|
bp = NULL; safe_realloc(bp, AALLOC_INITSZ);
|
2008-11-12 05:55:19 +01:00
|
|
|
/* ensure unaligned cookie */
|
2008-11-12 07:10:51 +01:00
|
|
|
#ifdef AALLOC_NO_COOKIES
|
2008-11-12 06:40:23 +01:00
|
|
|
bp->cookie = 0;
|
2008-11-12 07:10:51 +01:00
|
|
|
#else
|
|
|
|
do {
|
|
|
|
bp->cookie = AALLOC_RANDOM();
|
|
|
|
} while (!(bp->cookie & PVMASK));
|
|
|
|
#endif
|
2008-11-12 05:55:19 +01:00
|
|
|
|
|
|
|
/* first byte after block */
|
2008-11-12 06:11:05 +01:00
|
|
|
bp->endp = (char *)bp + AALLOC_INITSZ; /* bp + size of the block */
|
2008-11-12 05:55:19 +01:00
|
|
|
/* next entry (forward pointer) available for new allocation */
|
2008-11-12 06:11:05 +01:00
|
|
|
bp->last = (char *)&bp->storage; /* first entry */
|
2008-11-12 05:55:19 +01:00
|
|
|
|
2008-11-12 06:11:05 +01:00
|
|
|
ap->bp.pv = (char *)bp;
|
2008-11-12 05:55:19 +01:00
|
|
|
ap->bp.iv ^= gcookie;
|
|
|
|
#ifdef AALLOC_TRACK
|
2008-11-12 06:32:34 +01:00
|
|
|
ap->prev.pv = (char *)track;
|
|
|
|
ap->prev.iv ^= gcookie;
|
2008-11-12 05:55:19 +01:00
|
|
|
ap->ocookie = bp->cookie ^ gcookie;
|
|
|
|
track = ap;
|
|
|
|
#endif
|
|
|
|
AALLOC_DENY(bp);
|
|
|
|
return (ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate block in Area “ap”, return unlocked block pointer.
|
|
|
|
* If “ocookie” is not 0, make sure block cookie is equal.
|
|
|
|
*/
|
|
|
|
static PBlock
|
|
|
|
check_bp(PArea ap, const char *funcname, TCookie ocookie)
|
|
|
|
{
|
|
|
|
TPtr p;
|
|
|
|
PBlock bp;
|
|
|
|
|
|
|
|
if (ap->bp.pv == NULL) {
|
|
|
|
AALLOC_WARN("%s: area %p already freed", funcname, ap);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
p.iv = ap->bp.iv ^ gcookie;
|
2008-11-12 06:11:05 +01:00
|
|
|
if ((ptrdiff_t)(bp = (PBlock)p.pv) & PVMASK) {
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_WARN("%s: area %p block pointer destroyed: %p",
|
|
|
|
funcname, ap, p.pv);
|
2008-11-12 05:55:19 +01:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
AALLOC_PEEK(bp);
|
|
|
|
if (ocookie && bp->cookie != ocookie) {
|
2008-11-12 06:55:43 +01:00
|
|
|
AALLOC_WARN("%s: block %p cookie destroyed: %p, %p",
|
|
|
|
funcname, bp, (void *)ocookie, (void *)bp->cookie);
|
2008-11-12 05:55:19 +01:00
|
|
|
return (NULL);
|
|
|
|
}
|
2008-11-12 05:59:42 +01:00
|
|
|
if (((ptrdiff_t)bp->endp & PVMASK) || ((ptrdiff_t)bp->last & PVMASK)) {
|
2008-11-12 05:55:19 +01:00
|
|
|
AALLOC_WARN("%s: block %p data structure destroyed: %p, %p",
|
|
|
|
funcname, bp, bp->endp, bp->last);
|
|
|
|
return (NULL);
|
|
|
|
}
|
2008-11-12 06:11:05 +01:00
|
|
|
if (bp->endp < (char *)bp) {
|
2008-11-12 05:55:19 +01:00
|
|
|
AALLOC_WARN("%s: block %p end pointer out of bounds: %p",
|
|
|
|
funcname, bp, bp->endp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
2008-11-12 06:27:01 +01:00
|
|
|
if ((bp->last < (char *)&bp->storage) || (bp->last > bp->endp)) {
|
2008-11-12 05:55:19 +01:00
|
|
|
AALLOC_WARN("%s: block %p last pointer out of bounds: "
|
|
|
|
"%p < %p < %p", funcname, bp, &bp->storage, bp->last,
|
|
|
|
bp->endp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
AALLOC_ALLOW(bp);
|
|
|
|
return (bp);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef AALLOC_TRACK
|
|
|
|
/*
|
|
|
|
* At exit, dump and free any leaked allocations, blocks and areas.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
track_check(void)
|
|
|
|
{
|
|
|
|
PArea ap;
|
|
|
|
PBlock bp;
|
|
|
|
|
|
|
|
while (track) {
|
|
|
|
ap = track;
|
2008-11-12 06:32:34 +01:00
|
|
|
ap->ocookie ^= gcookie;
|
|
|
|
ap->prev.iv ^= gcookie;
|
2008-11-12 08:02:47 +01:00
|
|
|
if ((ap->prev.iv & PVMASK) || (ap->ocookie & PVMASK)) {
|
2008-11-12 05:55:19 +01:00
|
|
|
/* buffer overflow or something? */
|
|
|
|
AALLOC_WARN("AALLOC_TRACK data structure %p destroyed:"
|
2008-11-12 06:55:43 +01:00
|
|
|
" %p, %p, %p; exiting", ap, ap->prev.pv,
|
|
|
|
ap->bp.pv, (void *)ap->ocookie);
|
2008-11-12 05:55:19 +01:00
|
|
|
return;
|
|
|
|
}
|
2008-11-12 06:32:34 +01:00
|
|
|
if (!(bp = check_bp(ap, "atexit:track_check", ap->ocookie)))
|
2008-11-12 05:55:19 +01:00
|
|
|
goto track_next;
|
2008-11-12 07:44:04 +01:00
|
|
|
if (bp->last != (char *)&bp->storage)
|
2008-11-12 07:42:22 +01:00
|
|
|
#ifdef MKSH_VERSION /* allowed to leak silently */
|
|
|
|
adelete_leak(ap, bp, false, "at exit");
|
|
|
|
#else
|
2008-11-12 07:38:08 +01:00
|
|
|
adelete_leak(ap, bp, true, "at exit");
|
2008-11-12 07:44:04 +01:00
|
|
|
else
|
|
|
|
AALLOC_WARN("leaking empty area %p (%p %lu)", ap,
|
|
|
|
bp, (unsigned long)(bp->endp - (char *)bp));
|
2008-11-12 07:42:22 +01:00
|
|
|
#endif
|
2008-11-12 05:55:19 +01:00
|
|
|
free(bp);
|
|
|
|
track_next:
|
2008-11-12 06:32:34 +01:00
|
|
|
track = (PArea)ap->prev.pv;
|
2008-11-12 05:55:19 +01:00
|
|
|
free(ap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-11-12 06:05:17 +01:00
|
|
|
static void
|
2008-11-12 07:38:08 +01:00
|
|
|
adelete_leak(PArea ap, PBlock bp, bool always_warn, const char *when)
|
2008-11-12 06:05:17 +01:00
|
|
|
{
|
|
|
|
TPtr *cp;
|
|
|
|
|
2008-11-12 06:11:05 +01:00
|
|
|
while (bp->last > (char *)&bp->storage) {
|
2008-11-12 06:05:17 +01:00
|
|
|
bp->last -= PVALIGN;
|
|
|
|
cp = *((void **)bp->last);
|
|
|
|
cp->iv ^= bp->cookie;
|
2008-11-12 07:38:08 +01:00
|
|
|
if (always_warn || cp->pv != bp->last)
|
2008-11-12 07:14:26 +01:00
|
|
|
AALLOC_WARN("leaking %s pointer %p in area %p (%p %lu) %s",
|
2008-11-12 06:05:17 +01:00
|
|
|
cp->pv == bp->last ? "valid" : "underflown",
|
2008-11-12 06:55:43 +01:00
|
|
|
(char *)cp + PVALIGN, ap, bp,
|
2008-11-12 07:14:26 +01:00
|
|
|
(unsigned long)(bp->endp - (char *)bp), when);
|
2008-11-12 06:05:17 +01:00
|
|
|
free(cp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-12 05:55:19 +01:00
|
|
|
void
|
|
|
|
adelete(PArea *pap)
|
|
|
|
{
|
|
|
|
#ifdef AALLOC_TRACK
|
|
|
|
PArea tp;
|
|
|
|
#endif
|
|
|
|
PBlock bp;
|
|
|
|
|
2008-11-12 06:05:17 +01:00
|
|
|
/* ignore invalid areas */
|
2008-11-12 07:12:56 +01:00
|
|
|
if ((bp = check_bp(*pap, "adelete", 0)) != NULL) {
|
2008-11-12 06:11:05 +01:00
|
|
|
if (bp->last != (char *)&bp->storage)
|
2008-11-12 07:38:08 +01:00
|
|
|
adelete_leak(*pap, bp, false, "in adelete");
|
2008-11-12 06:05:17 +01:00
|
|
|
free(bp);
|
|
|
|
}
|
2008-11-12 05:55:19 +01:00
|
|
|
|
|
|
|
#ifdef AALLOC_TRACK
|
|
|
|
if (track == *pap) {
|
|
|
|
(*pap)->prev.iv ^= gcookie;
|
2008-11-12 06:32:34 +01:00
|
|
|
track = (PArea)((*pap)->prev.pv);
|
2008-11-12 05:55:19 +01:00
|
|
|
goto adelete_tracked;
|
|
|
|
}
|
|
|
|
/* find the TArea whose prev is *pap */
|
|
|
|
tp = track;
|
|
|
|
while (tp) {
|
|
|
|
TPtr lp;
|
|
|
|
lp.iv = tp->prev.iv ^ gcookie;
|
2008-11-12 08:02:47 +01:00
|
|
|
if ((lp.iv & PVMASK) || (tp->ocookie & PVMASK)) {
|
2008-11-12 05:55:19 +01:00
|
|
|
AALLOC_WARN("AALLOC_TRACK data structure %p destroyed:"
|
2008-11-12 06:55:43 +01:00
|
|
|
" %p, %p, %p", tp, tp->prev.pv, tp->bp.pv,
|
|
|
|
(void *)tp->ocookie);
|
2008-11-12 05:55:19 +01:00
|
|
|
tp = NULL;
|
|
|
|
break;
|
|
|
|
}
|
2008-11-12 06:32:34 +01:00
|
|
|
if (lp.pv == (char *)*pap)
|
2008-11-12 05:55:19 +01:00
|
|
|
break;
|
2008-11-12 06:32:34 +01:00
|
|
|
tp = (PArea)lp.pv;
|
2008-11-12 05:55:19 +01:00
|
|
|
}
|
|
|
|
if (tp)
|
|
|
|
tp->prev.iv = (*pap)->prev.iv; /* decouple *pap */
|
|
|
|
else
|
|
|
|
AALLOC_WARN("area %p not in found AALLOC_TRACK data structure",
|
|
|
|
*pap);
|
|
|
|
adelete_tracked:
|
|
|
|
#endif
|
|
|
|
free(*pap);
|
|
|
|
*pap = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
alloc(size_t nmemb, size_t size, PArea ap)
|
|
|
|
{
|
|
|
|
PBlock bp;
|
|
|
|
TPtr *ptr;
|
|
|
|
|
|
|
|
/* obtain the memory region requested, retaining guards */
|
|
|
|
safe_muladd(nmemb, size, sizeof (TPtr));
|
|
|
|
ptr = NULL; safe_realloc(ptr, size);
|
|
|
|
|
|
|
|
/* chain into area */
|
|
|
|
if ((bp = check_bp(ap, "alloc", 0)) == NULL)
|
|
|
|
AALLOC_ABORT("cannot continue");
|
|
|
|
if (bp->last == bp->endp) {
|
2008-11-12 07:58:49 +01:00
|
|
|
TPtr **tp;
|
2008-11-12 05:55:19 +01:00
|
|
|
size_t bsz;
|
|
|
|
|
|
|
|
/* make room for more forward ptrs in the block allocation */
|
2008-11-12 06:11:05 +01:00
|
|
|
bsz = bp->endp - (char *)bp;
|
2008-11-12 06:55:43 +01:00
|
|
|
safe_muladd(2, bsz, 0);
|
2008-11-12 05:55:19 +01:00
|
|
|
safe_realloc(bp, bsz);
|
2008-11-12 06:27:01 +01:00
|
|
|
bp->last = (char *)bp + (bsz / 2);
|
|
|
|
bp->endp = (char *)bp + bsz;
|
2008-11-12 05:55:19 +01:00
|
|
|
|
2008-11-12 06:45:28 +01:00
|
|
|
/* all backpointers have to be adjusted */
|
2008-11-12 07:58:49 +01:00
|
|
|
for (tp = (TPtr **)&bp->storage; tp < (TPtr **)bp->last; ++tp) {
|
|
|
|
(*tp)->pv = (char *)tp;
|
|
|
|
(*tp)->iv ^= bp->cookie;
|
2008-11-12 06:45:28 +01:00
|
|
|
}
|
|
|
|
|
2008-11-12 05:55:19 +01:00
|
|
|
/* “bp” has possibly changed, enter its new value into ap */
|
2008-11-12 06:11:05 +01:00
|
|
|
ap->bp.pv = (char *)bp;
|
2008-11-12 05:55:19 +01:00
|
|
|
ap->bp.iv ^= gcookie;
|
|
|
|
}
|
|
|
|
*((void **)bp->last) = ptr; /* next available forward ptr */
|
|
|
|
ptr->pv = bp->last; /* backpointer to fwdptr storage */
|
|
|
|
ptr->iv ^= bp->cookie; /* apply block cookie */
|
|
|
|
bp->last += PVALIGN; /* advance next-avail pointer */
|
|
|
|
AALLOC_DENY(bp);
|
|
|
|
|
|
|
|
/* return aligned storage just after the cookied backpointer */
|
|
|
|
return ((char *)ptr + PVALIGN);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
aresize(void *vp, size_t nmemb, size_t size, PArea ap)
|
|
|
|
{
|
|
|
|
PBlock bp;
|
|
|
|
TPtr *ptr;
|
|
|
|
|
|
|
|
if (vp == NULL)
|
|
|
|
return (alloc(nmemb, size, ap));
|
|
|
|
|
|
|
|
/* validate allocation and backpointer against forward pointer */
|
|
|
|
if ((ptr = check_ptr(vp, ap, &bp, "aresize", "")) == NULL)
|
|
|
|
AALLOC_ABORT("cannot continue");
|
|
|
|
|
|
|
|
/* move allocation to size and possibly new location */
|
|
|
|
safe_muladd(nmemb, size, sizeof (TPtr));
|
|
|
|
safe_realloc(ptr, size);
|
|
|
|
|
|
|
|
/* write new address of allocation into the block forward pointer */
|
|
|
|
memcpy(ptr->pv, &ptr, PVALIGN);
|
|
|
|
/* apply the cookie on the backpointer again */
|
|
|
|
ptr->iv ^= bp->cookie;
|
|
|
|
AALLOC_DENY(bp);
|
|
|
|
|
|
|
|
return ((char *)ptr + PVALIGN);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to find “vp” inside Area “ap”, use “what” and “extra” for error msgs.
|
|
|
|
*
|
|
|
|
* If an error occurs, returns NULL with no side effects.
|
|
|
|
* Otherwise, returns address of the allocation, with the cookie on the
|
|
|
|
* backpointer unapplied; *bpp contains the unlocked block pointer.
|
|
|
|
*/
|
|
|
|
static TPtr *
|
|
|
|
check_ptr(void *vp, PArea ap, PBlock *bpp, const char *what, const char *extra)
|
|
|
|
{
|
|
|
|
PBlock bp;
|
|
|
|
TPtr *ptr;
|
|
|
|
|
2008-11-12 05:59:42 +01:00
|
|
|
if ((ptrdiff_t)vp & PVMASK) {
|
2008-11-12 05:55:19 +01:00
|
|
|
AALLOC_WARN("trying to %s rogue unaligned pointer %p from "
|
|
|
|
"area %p%s", what + 1, vp, ap, extra);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = (TPtr *)((char *)vp - PVALIGN);
|
|
|
|
if (!ptr->iv) {
|
|
|
|
AALLOC_WARN("trying to %s already freed pointer %p from "
|
|
|
|
"area %p%s", what + 1, vp, ap, extra);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bp = check_bp(ap, what, 0)) == NULL)
|
|
|
|
AALLOC_ABORT("cannot continue");
|
|
|
|
ptr->iv ^= bp->cookie;
|
2008-11-12 06:11:05 +01:00
|
|
|
if (ptr->pv < (char *)&bp->storage || ptr->pv >= bp->last) {
|
2008-11-12 05:55:19 +01:00
|
|
|
AALLOC_WARN("trying to %s rogue pointer %p from area %p "
|
|
|
|
"(block %p..%p), backpointer %p out of bounds%s",
|
|
|
|
what + 1, vp, ap, bp, bp->last, ptr->pv, extra);
|
|
|
|
AALLOC_DENY(bp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
if (*((void **)ptr->pv) != ptr) {
|
|
|
|
AALLOC_WARN("trying to %s rogue pointer %p from area %p "
|
|
|
|
"(block %p..%p), backpointer %p, forward pointer to "
|
|
|
|
"%p instead%s", what + 1, vp, ap, bp, bp->last,
|
|
|
|
ptr->pv, *((void **)ptr->pv), extra);
|
|
|
|
AALLOC_DENY(bp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
*bpp = bp;
|
|
|
|
return (ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
afree(void *vp, PArea ap)
|
|
|
|
{
|
|
|
|
PBlock bp;
|
|
|
|
TPtr *ptr;
|
|
|
|
|
|
|
|
if (vp == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* validate allocation and backpointer, ignore rogues */
|
|
|
|
if ((ptr = check_ptr(vp, ap, &bp, "afree", ", ignoring")) == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* note: the block allocation does not ever shrink */
|
|
|
|
bp->last -= PVALIGN; /* mark the last forward pointer as free */
|
|
|
|
/* if our forward pointer was not the last one, relocate the latter */
|
|
|
|
if (ptr->pv < bp->last) {
|
2008-11-12 07:35:27 +01:00
|
|
|
TPtr *tmp = *((TPtr **)bp->last);
|
2008-11-12 05:55:19 +01:00
|
|
|
|
2008-11-12 07:35:27 +01:00
|
|
|
/* tmp is the former last forward pointer */
|
2008-11-12 05:55:19 +01:00
|
|
|
tmp->pv = ptr->pv; /* its backpointer to former our … */
|
|
|
|
tmp->iv ^= bp->cookie; /* … forward pointer, and cookie it */
|
|
|
|
|
|
|
|
memcpy(ptr->pv, bp->last, PVALIGN); /* relocate fwd ptr */
|
|
|
|
}
|
|
|
|
ptr->iv = 0; /* our backpointer, just in case, for double frees */
|
|
|
|
free(ptr);
|
|
|
|
|
|
|
|
AALLOC_DENY(bp);
|
|
|
|
return;
|
|
|
|
}
|