prepare for aalloc.c, which I have just written myself, as an area-based
allocator using malloc and free, with mmap malloc and omalloc in mind, not counterfeiting its security measures such as guard pages, and having some of our own, e.g. XOR random cookies, optional mprotect, etc. zero cost (for we have arc4random())
This commit is contained in:
parent
8d5d720f08
commit
3c1e46ee4d
470
aalloc.c
Normal file
470
aalloc.c
Normal file
@ -0,0 +1,470 @@
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/aalloc.c,v 1.1 2008/11/12 04:55:17 tg Exp $");
|
||||
|
||||
/* 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
|
||||
#define AALLOC_INITSZ 64 /* must hold at least 4 pointers */
|
||||
#endif
|
||||
|
||||
typedef /* unsigned */ ptrdiff_t TCookie;
|
||||
|
||||
typedef union {
|
||||
TCookie iv;
|
||||
void *pv;
|
||||
} 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;
|
||||
void *endp;
|
||||
void *last;
|
||||
void *storage;
|
||||
};
|
||||
typedef struct TBlock *PBlock;
|
||||
|
||||
struct TArea {
|
||||
TPtr bp;
|
||||
#ifdef AALLOC_TRACK
|
||||
TPtr prev;
|
||||
TCookie ocookie;
|
||||
#endif
|
||||
};
|
||||
|
||||
static TCookie gcookie;
|
||||
|
||||
#ifdef AALLOC_TRACK
|
||||
static PArea track;
|
||||
static void track_check(void);
|
||||
#endif
|
||||
|
||||
#ifdef AALLOC_MPROTECT
|
||||
#undef AALLOC_INITSZ
|
||||
#define AALLOC_INITSZ pagesz
|
||||
static size_t pagesz;
|
||||
#define AALLOC_ACCESS(bp, n, p) mprotect((bp), (bp)->endp - (bp), (p))
|
||||
#define AALLOC_ALLOW(bp) \
|
||||
AALLOC_ACCESS((bp), (bp)->endp - (bp), PROT_READ | PROT_WRITE);
|
||||
#define AALLOC_DENY(bp) \
|
||||
AALLOC_ACCESS((bp), (bp)->endp - (bp), PROT_NONE)
|
||||
#define AALLOC_PEEK(bp) \
|
||||
AALLOC_ACCESS((bp), sizeof (struct TArea), PROT_READ | PROT_WRITE)
|
||||
#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) \
|
||||
AALLOC_ABORT("unable to allocate %zu bytes: %s", \
|
||||
(len), strerror(errno)); \
|
||||
if ((dest) & PVMASK) \
|
||||
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) \
|
||||
AALLOC_ABORT("attempted integer overflow:" \
|
||||
" %zu * %zu", nmemb, size); \
|
||||
size *= nmemb; \
|
||||
if (size >= SIZE_MAX - extra) \
|
||||
AALLOC_ABORT("unable to allocate %zu bytes: %s", \
|
||||
size, "value plus extra too big"); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
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;
|
||||
|
||||
#ifdef AALLOC_DEBUG
|
||||
if (PVALIGN != 2 && PVALIGN != 4 && PVALIGN != 8 && PVALIGN != 16)
|
||||
AALLOC_ABORT("PVALIGN not a power of two: %zu", PVALIGN);
|
||||
if (sizeof (TPtr) != sizeof (TCookie) || sizeof (TPtr) != PVALIGN)
|
||||
AALLOC_ABORT("TPtr sizes do not match: %zu, %zu, %zu",
|
||||
sizeof (TPtr), sizeof (TCookie), PVALIGN);
|
||||
if (AALLOC_INITSZ < sizeof (struct TBlock))
|
||||
AALLOC_ABORT("AALLOC_INITSZ constant too small: %zu < %zu",
|
||||
AALLOC_INITSZ, sizeof (struct TBlock));
|
||||
#endif
|
||||
|
||||
#ifdef AALLOC_MPROTECT
|
||||
if (!pagesz) {
|
||||
if ((pagesz = sysconf(_SC_PAGESIZE)) == (size_t)-1 ||
|
||||
pagesz < PVALIGN)
|
||||
AALLOC_ABORT("sysconf(_SC_PAGESIZE) failed: %zd %s",
|
||||
pagesz, strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!gcookie) {
|
||||
size_t v;
|
||||
|
||||
/* ensure unaligned cookie */
|
||||
do {
|
||||
gcookie = AALLOC_RANDOM();
|
||||
v = AALLOC_RANDOM() & 7;
|
||||
} while (!(gcookie & PVMASK) || !v);
|
||||
/* randomise seed afterwards */
|
||||
while (v--)
|
||||
AALLOC_RANDOM();
|
||||
#ifdef AALLOC_TRACK
|
||||
atexit(track_check);
|
||||
#endif
|
||||
}
|
||||
|
||||
ap = NULL; safe_realloc(ap, sizeof (struct TArea));
|
||||
bp = NULL; safe_realloc(bp, AALLOC_INITSZ);
|
||||
/* ensure unaligned cookie */
|
||||
do {
|
||||
bp->cookie = AALLOC_RANDOM();
|
||||
} while (!(bp->cookie & PVMASK));
|
||||
|
||||
/* first byte after block */
|
||||
bp->endp = bp + AALLOC_INITSZ; /* bp + size of the block */
|
||||
/* next entry (forward pointer) available for new allocation */
|
||||
bp->last = &bp->storage; /* first entry */
|
||||
|
||||
ap->bp.pv = bp;
|
||||
ap->bp.iv ^= gcookie;
|
||||
#ifdef AALLOC_TRACK
|
||||
ap->prev.pv = track;
|
||||
ap->prev.iv ^= track_cookie;
|
||||
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;
|
||||
if ((bp = p.pv) & PVMASK) {
|
||||
AALLOC_WARN("%s: area %p block pointer destroyed: %08X",
|
||||
funcname, ap, p.iv);
|
||||
return (NULL);
|
||||
}
|
||||
AALLOC_PEEK(bp);
|
||||
if (ocookie && bp->cookie != ocookie) {
|
||||
AALLOC_WARN("%s: block %p cookie destroyed: %08X, %08X",
|
||||
funcname, bp, ocookie, bp->cookie);
|
||||
return (NULL);
|
||||
}
|
||||
if ((bp->endp & PVMASK) || (bp->last & PVMASK)) {
|
||||
AALLOC_WARN("%s: block %p data structure destroyed: %p, %p",
|
||||
funcname, bp, bp->endp, bp->last);
|
||||
return (NULL);
|
||||
}
|
||||
if (bp->endp < bp) {
|
||||
AALLOC_WARN("%s: block %p end pointer out of bounds: %p",
|
||||
funcname, bp, bp->endp);
|
||||
return (NULL);
|
||||
}
|
||||
if ((bp->last < &bp->storage) || (bp->last >= bp->endp)) {
|
||||
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;
|
||||
ap->ocookie ^= track_cookie;
|
||||
ap->prev.iv ^= track_cookie;
|
||||
if ((ap->prev.iv & PVMASK) || !ap->ocookie) {
|
||||
/* buffer overflow or something? */
|
||||
AALLOC_WARN("AALLOC_TRACK data structure %p destroyed:"
|
||||
" %p, %08X, %08X", ap, ap->prev.pv,
|
||||
ap->bp.iv, ap->ocookie);
|
||||
return;
|
||||
}
|
||||
if (!(bp = check_bp(ap, "atexit:track_check", tp->ocookie)))
|
||||
goto track_next;
|
||||
if (bp->last == &bp->storage) {
|
||||
AALLOC_WARN("leaking empty area %p (%p %tu)", ap,
|
||||
bp, bp->endp - bp);
|
||||
goto track_freebp;
|
||||
}
|
||||
while (bp->last > &bp->storage) {
|
||||
TPtr *cp;
|
||||
|
||||
bp->last -= PVALIGN;
|
||||
cp = *((void **)bp->last);
|
||||
cp->iv ^= bp->cookie;
|
||||
AALLOC_WARN("leaking %s pointer %p in area %p (%p %tu)",
|
||||
cp->pv == bp->last ? "valid" : "underflown",
|
||||
(char *)cp + PVALIGN, ap, bp, bp->endp - bp);
|
||||
free(cp);
|
||||
}
|
||||
track_freebp:
|
||||
free(bp);
|
||||
track_next:
|
||||
track = ap->prev.pv;
|
||||
free(ap);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
adelete(PArea *pap)
|
||||
{
|
||||
#ifdef AALLOC_TRACK
|
||||
PArea tp;
|
||||
#endif
|
||||
PBlock bp;
|
||||
|
||||
if ((bp = check_bp(*pap, "adelete", 0)) == NULL)
|
||||
goto adelete_freeap; /* ignore invalid areas */
|
||||
|
||||
if (bp->last != &bp->storage)
|
||||
adelete_leak(bp);
|
||||
free(bp);
|
||||
adelete_freeap:
|
||||
#ifdef AALLOC_TRACK
|
||||
if (track == *pap) {
|
||||
(*pap)->prev.iv ^= gcookie;
|
||||
track = (*pap)->prev.pv;
|
||||
goto adelete_tracked;
|
||||
}
|
||||
/* find the TArea whose prev is *pap */
|
||||
tp = track;
|
||||
while (tp) {
|
||||
TPtr lp;
|
||||
lp.iv = tp->prev.iv ^ gcookie;
|
||||
if ((lp.iv & PVMASK) || !lp->ocookie) {
|
||||
AALLOC_WARN("AALLOC_TRACK data structure %p destroyed:"
|
||||
" %p, %08X, %08X", tp, tp->prev.pv,
|
||||
tp->bp.iv, tp->ocookie);
|
||||
tp = NULL;
|
||||
break;
|
||||
}
|
||||
if (lp.pv == *pap)
|
||||
break;
|
||||
tp = lp.pv;
|
||||
}
|
||||
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) {
|
||||
size_t bsz;
|
||||
|
||||
/* make room for more forward ptrs in the block allocation */
|
||||
bsz = bp->endp - bp;
|
||||
safe_muladd(2, bsz, 0);
|
||||
safe_realloc(bp, bsz);
|
||||
|
||||
/* “bp” has possibly changed, enter its new value into ap */
|
||||
ap->bp.pv = bp;
|
||||
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;
|
||||
|
||||
if (vp & PVMASK) {
|
||||
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;
|
||||
if (ptr->pv < &bp->storage || ptr->pv >= bp->last) {
|
||||
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) {
|
||||
TPtr *tmp = bp->last; /* former last forward pointer */
|
||||
|
||||
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;
|
||||
}
|
4
check.t
4
check.t
@ -1,4 +1,4 @@
|
||||
# $MirOS: src/bin/mksh/check.t,v 1.242 2008/11/10 19:33:07 tg Exp $
|
||||
# $MirOS: src/bin/mksh/check.t,v 1.243 2008/11/12 04:55:17 tg Exp $
|
||||
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
|
||||
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
|
||||
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
|
||||
@ -7,7 +7,7 @@
|
||||
# http://www.research.att.com/~gsf/public/ifs.sh
|
||||
|
||||
expected-stdout:
|
||||
@(#)MIRBSD KSH R36 2008/11/10
|
||||
@(#)MIRBSD KSH R36 2008/11/11
|
||||
description:
|
||||
Check version of shell.
|
||||
stdin:
|
||||
|
6
main.c
6
main.c
@ -13,7 +13,7 @@
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.109 2008/11/12 00:54:49 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.110 2008/11/12 04:55:18 tg Exp $");
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@ -76,6 +76,10 @@ main(int argc, const char *argv[])
|
||||
char *cp;
|
||||
#endif
|
||||
|
||||
#if !HAVE_ARC4RANDOM
|
||||
change_random((unsigned long)time(NULL) * getpid());
|
||||
#endif
|
||||
|
||||
/* make sure argv[] is sane */
|
||||
if (!*argv) {
|
||||
static const char *empty_argv[] = {
|
||||
|
4
sh.h
4
sh.h
@ -103,9 +103,9 @@
|
||||
#define __SCCSID(x) __IDSTRING(sccsid,x)
|
||||
|
||||
#ifdef EXTERN
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.256 2008/11/12 00:55:32 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.257 2008/11/12 04:55:19 tg Exp $");
|
||||
#endif
|
||||
#define MKSH_VERSION "R36 2008/11/10"
|
||||
#define MKSH_VERSION "R36 2008/11/11"
|
||||
|
||||
#ifndef MKSH_INCLUDES_ONLY
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user