|  | /* | 
|  | * Electric Fence - Red-Zone memory allocator. | 
|  | * Bruce Perens, 1988, 1993 | 
|  | * | 
|  | * For email below, drop spaces and <spam-buster> tag. | 
|  | * MODIFIED:  March 20, 2014 (jric<spam-buster> @ <spam-buster> chegg DOT com) | 
|  | * | 
|  | * This is a special version of malloc() and company for debugging software | 
|  | * that is suspected of overrunning or underrunning the boundaries of a | 
|  | * malloc buffer, or touching free memory. | 
|  | * | 
|  | * It arranges for each malloc buffer to be followed (or preceded) | 
|  | * in the address space by an inaccessable virtual memory page, | 
|  | * and for free memory to be inaccessable. If software touches the | 
|  | * inaccessable page, it will get an immediate segmentation | 
|  | * fault. It is then trivial to uncover the offending code using a debugger. | 
|  | * | 
|  | * An advantage of this product over most malloc debuggers is that this one | 
|  | * detects reading out of bounds as well as writing, and this one stops on | 
|  | * the exact instruction that causes the error, rather than waiting until the | 
|  | * next boundary check. | 
|  | * | 
|  | * There is one product that debugs malloc buffer overruns | 
|  | * better than Electric Fence: "Purify" from Purify Systems, and that's only | 
|  | * a small part of what Purify does. I'm not affiliated with Purify, I just | 
|  | * respect a job well done. | 
|  | * | 
|  | * This version of malloc() should not be linked into production software, | 
|  | * since it tremendously increases the time and memory overhead of malloc(). | 
|  | * Each malloc buffer will consume a minimum of two virtual memory pages, | 
|  | * this is 16 kilobytes on many systems. On some systems it will be necessary | 
|  | * to increase the amount of swap space in order to debug large programs that | 
|  | * perform lots of allocation, because of the per-buffer overhead. | 
|  | * | 
|  | */ | 
|  | #include "efence.h" | 
|  | #include <errno.h> | 
|  | #include <memory.h> | 
|  | #include <pthread.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <parlib/spinlock.h> | 
|  | #include <parlib/stdio.h> | 
|  |  | 
|  | /* | 
|  | * MEMORY_CREATION_SIZE is the amount of memory to get from the operating | 
|  | * system at one time. We'll break that memory down into smaller pieces for | 
|  | * malloc buffers. One megabyte is probably a good value. | 
|  | */ | 
|  | #define MEMORY_CREATION_SIZE 1024 * 1024 | 
|  |  | 
|  | /* | 
|  | * Enum Mode indicates the status of a malloc buffer. | 
|  | */ | 
|  | enum _Mode { | 
|  | NOT_IN_USE = 0, /* Available to represent a malloc buffer. */ | 
|  | FREE,           /* A free buffer. */ | 
|  | ALLOCATED,      /* A buffer that is in use. */ | 
|  | PROTECTED,      /* A freed buffer that can not be allocated again. */ | 
|  | INTERNAL_USE    /* A buffer used internally by malloc(). */ | 
|  | }; | 
|  | typedef enum _Mode Mode; | 
|  |  | 
|  | /* | 
|  | * Struct Slot contains all of the information about a malloc buffer except | 
|  | * for the contents of its memory. | 
|  | */ | 
|  | struct _Slot { | 
|  | void *userAddress; | 
|  | void *internalAddress; | 
|  | size_t userSize; | 
|  | size_t internalSize; | 
|  | Mode mode; | 
|  | }; | 
|  | typedef struct _Slot Slot; | 
|  |  | 
|  | /* | 
|  | * EF_ALIGNMENT is a global variable used to control the default alignment | 
|  | * of buffers returned by malloc(), calloc(), and realloc(). It is all-caps | 
|  | * so that its name matches the name of the environment variable that is used | 
|  | * to set it. This gives the programmer one less name to remember. | 
|  | * If the value is -1, it will be set from the environment or sizeof(int) | 
|  | * at run time. | 
|  | */ | 
|  | int EF_ALIGNMENT = -1; | 
|  |  | 
|  | /* | 
|  | * EF_PROTECT_FREE is a global variable used to control the disposition of | 
|  | * memory that is released using free(). It is all-caps so that its name | 
|  | * matches the name of the environment variable that is used to set it. | 
|  | * If its value is greater non-zero, memory released by free is made | 
|  | * inaccessable and never allocated again. Any software that touches free | 
|  | * memory will then get a segmentation fault. If its value is zero, freed | 
|  | * memory will be available for reallocation, but will still be inaccessable | 
|  | * until it is reallocated. | 
|  | * If the value is -1, it will be set from the environment or to 0 at run-time. | 
|  | */ | 
|  | int EF_PROTECT_FREE = -1; | 
|  |  | 
|  | /* | 
|  | * EF_PROTECT_BELOW is used to modify the behavior of the allocator. When | 
|  | * its value is non-zero, the allocator will place an inaccessable page | 
|  | * immediately _before_ the malloc buffer in the address space, instead | 
|  | * of _after_ it. Use this to detect malloc buffer under-runs, rather than | 
|  | * over-runs. It won't detect both at the same time, so you should test your | 
|  | * software twice, once with this value clear, and once with it set. | 
|  | * If the value is -1, it will be set from the environment or to zero at | 
|  | * run-time | 
|  | */ | 
|  | int EF_PROTECT_BELOW = -1; | 
|  |  | 
|  | /* | 
|  | * EF_ALLOW_MALLOC_0 is set if Electric Fence is to allow malloc(0). I | 
|  | * trap malloc(0) by default because it is a common source of bugs. | 
|  | */ | 
|  | int EF_ALLOW_MALLOC_0 = -1; | 
|  |  | 
|  | /* | 
|  | * EF_FREE_WIPES is set if Electric Fence is to wipe the memory content | 
|  | * of freed blocks.  This makes it easier to check if memory is freed or | 
|  | * not | 
|  | */ | 
|  | int EF_FREE_WIPES = -1; | 
|  |  | 
|  | /* | 
|  | * allocationList points to the array of slot structures used to manage the | 
|  | * malloc arena. | 
|  | */ | 
|  | static Slot *allocationList = 0; | 
|  |  | 
|  | /* | 
|  | * allocationListSize is the size of the allocation list. This will always | 
|  | * be a multiple of the page size. | 
|  | */ | 
|  | static size_t allocationListSize = 0; | 
|  |  | 
|  | /* | 
|  | * slotCount is the number of Slot structures in allocationList. | 
|  | */ | 
|  | static size_t slotCount = 0; | 
|  |  | 
|  | /* | 
|  | * unUsedSlots is the number of Slot structures that are currently available | 
|  | * to represent new malloc buffers. When this number gets too low, we will | 
|  | * create new slots. | 
|  | */ | 
|  | static size_t unUsedSlots = 0; | 
|  |  | 
|  | /* | 
|  | * slotsPerPage is the number of slot structures that fit in a virtual | 
|  | * memory page. | 
|  | */ | 
|  | static size_t slotsPerPage = 0; | 
|  |  | 
|  | /* | 
|  | * internalUse is set when allocating and freeing the allocatior-internal | 
|  | * data structures. | 
|  | */ | 
|  | static int internalUse = 0; | 
|  |  | 
|  | /* | 
|  | * noAllocationListProtection is set to tell malloc() and free() not to | 
|  | * manipulate the protection of the allocation list. This is only set in | 
|  | * realloc(), which does it to save on slow system calls, and in | 
|  | * allocateMoreSlots(), which does it because it changes the allocation list. | 
|  | */ | 
|  | static int noAllocationListProtection = 0; | 
|  |  | 
|  | /* | 
|  | * bytesPerPage is set at run-time to the number of bytes per virtual-memory | 
|  | * page, as returned by Page_Size(). | 
|  | */ | 
|  | static size_t bytesPerPage = 0; | 
|  |  | 
|  | /* | 
|  | * mutex to enable multithreaded operation | 
|  | */ | 
|  | static struct spin_pdr_lock pdr_lock = SPINPDR_INITIALIZER; | 
|  | #define RECURSE_UNLOCKED -1 | 
|  | static long lockholder = RECURSE_UNLOCKED; | 
|  | static int lock_depth; | 
|  |  | 
|  | static long who_am_i(void) | 
|  | { | 
|  | if (in_vcore_context()) | 
|  | return vcore_id(); | 
|  | else | 
|  | return (long)current_uthread; | 
|  | } | 
|  |  | 
|  | static void lock(void) | 
|  | { | 
|  | long me = who_am_i(); | 
|  |  | 
|  | if (!spin_pdr_trylock(&pdr_lock)) { | 
|  | if (lockholder == me) { | 
|  | lock_depth++; | 
|  | return; | 
|  | } | 
|  | spin_pdr_lock(&pdr_lock); | 
|  | } | 
|  | lockholder = me; | 
|  | lock_depth = 1; | 
|  | } | 
|  |  | 
|  | static void unlock(void) | 
|  | { | 
|  | lock_depth--; | 
|  | if (!lock_depth) { | 
|  | lockholder = RECURSE_UNLOCKED; | 
|  | spin_pdr_unlock(&pdr_lock); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * internalError is called for those "shouldn't happen" errors in the | 
|  | * allocator. | 
|  | */ | 
|  | static void internalError(void) | 
|  | { | 
|  | EF_Abort("Internal error in allocator."); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * initialize sets up the memory allocation arena and the run-time | 
|  | * configuration information. | 
|  | */ | 
|  | static void initialize(void) | 
|  | { | 
|  | size_t size = MEMORY_CREATION_SIZE; | 
|  | size_t slack; | 
|  | char *string; | 
|  | Slot *slot; | 
|  |  | 
|  | /* | 
|  | * Import the user's environment specification of the default | 
|  | * alignment for malloc(). We want that alignment to be under | 
|  | * user control, since smaller alignment lets us catch more bugs, | 
|  | * however some software will break if malloc() returns a buffer | 
|  | * that is not word-aligned. | 
|  | * | 
|  | * I would like | 
|  | * alignment to be zero so that we could catch all one-byte | 
|  | * overruns, however if malloc() is asked to allocate an odd-size | 
|  | * buffer and returns an address that is not word-aligned, or whose | 
|  | * size is not a multiple of the word size, software breaks. | 
|  | * This was the case with the Sun string-handling routines, | 
|  | * which can do word fetches up to three bytes beyond the end of a | 
|  | * string. I handle this problem in part by providing | 
|  | * byte-reference-only versions of the string library functions, but | 
|  | * there are other functions that break, too. Some in X Windows, one | 
|  | * in Sam Leffler's TIFF library, and doubtless many others. | 
|  | */ | 
|  | if (EF_ALIGNMENT == -1) { | 
|  | if ((string = getenv("EF_ALIGNMENT")) != 0) | 
|  | EF_ALIGNMENT = (size_t)atoi(string); | 
|  | else | 
|  | EF_ALIGNMENT = sizeof(int); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * See if the user wants to protect the address space below a buffer, | 
|  | * rather than that above a buffer. | 
|  | */ | 
|  | if (EF_PROTECT_BELOW == -1) { | 
|  | if ((string = getenv("EF_PROTECT_BELOW")) != 0) | 
|  | EF_PROTECT_BELOW = (atoi(string) != 0); | 
|  | else | 
|  | EF_PROTECT_BELOW = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * See if the user wants to protect memory that has been freed until | 
|  | * the program exits, rather than until it is re-allocated. | 
|  | */ | 
|  | if (EF_PROTECT_FREE == -1) { | 
|  | if ((string = getenv("EF_PROTECT_FREE")) != 0) | 
|  | EF_PROTECT_FREE = (atoi(string) != 0); | 
|  | else | 
|  | EF_PROTECT_FREE = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * See if the user wants to allow malloc(0). | 
|  | */ | 
|  | if (EF_ALLOW_MALLOC_0 == -1) { | 
|  | if ((string = getenv("EF_ALLOW_MALLOC_0")) != 0) | 
|  | EF_ALLOW_MALLOC_0 = (atoi(string) != 0); | 
|  | else | 
|  | EF_ALLOW_MALLOC_0 = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * See if the user wants us to wipe out freed memory. | 
|  | */ | 
|  | if (EF_FREE_WIPES == -1) { | 
|  | if ((string = getenv("EF_FREE_WIPES")) != 0) | 
|  | EF_FREE_WIPES = (atoi(string) != 0); | 
|  | else | 
|  | EF_FREE_WIPES = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Get the run-time configuration of the virtual memory page size. | 
|  | */ | 
|  | bytesPerPage = Page_Size(); | 
|  |  | 
|  | /* | 
|  | * Figure out how many Slot structures to allocate at one time. | 
|  | */ | 
|  | slotCount = slotsPerPage = bytesPerPage / sizeof(Slot); | 
|  | allocationListSize = bytesPerPage; | 
|  |  | 
|  | if (allocationListSize > size) | 
|  | size = allocationListSize; | 
|  |  | 
|  | if ((slack = size % bytesPerPage) != 0) | 
|  | size += bytesPerPage - slack; | 
|  |  | 
|  | /* | 
|  | * Allocate memory, and break it up into two malloc buffers. The | 
|  | * first buffer will be used for Slot structures, the second will | 
|  | * be marked free. | 
|  | */ | 
|  | slot = allocationList = (Slot *)Page_Create(size); | 
|  | memset((char *)allocationList, 0, allocationListSize); | 
|  |  | 
|  | slot[0].internalSize = slot[0].userSize = allocationListSize; | 
|  | slot[0].internalAddress = slot[0].userAddress = allocationList; | 
|  | slot[0].mode = INTERNAL_USE; | 
|  | if (size > allocationListSize) { | 
|  | slot[1].internalAddress = slot[1].userAddress = | 
|  | ((char *)slot[0].internalAddress) + slot[0].internalSize; | 
|  | slot[1].internalSize = slot[1].userSize | 
|  | = size - slot[0].internalSize; | 
|  | slot[1].mode = FREE; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Deny access to the free page, so that we will detect any software | 
|  | * that treads upon free memory. | 
|  | */ | 
|  | Page_DenyAccess(slot[1].internalAddress, slot[1].internalSize); | 
|  |  | 
|  | /* | 
|  | * Account for the two slot structures that we've used. | 
|  | */ | 
|  | unUsedSlots = slotCount - 2; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * allocateMoreSlots is called when there are only enough slot structures | 
|  | * left to support the allocation of a single malloc buffer. | 
|  | */ | 
|  | static void allocateMoreSlots(void) | 
|  | { | 
|  | size_t newSize = allocationListSize + bytesPerPage; | 
|  | void *newAllocation; | 
|  | void *oldAllocation = allocationList; | 
|  |  | 
|  | Page_AllowAccess(allocationList, allocationListSize); | 
|  | noAllocationListProtection = 1; | 
|  | internalUse = 1; | 
|  |  | 
|  | newAllocation = malloc(newSize); | 
|  | memcpy(newAllocation, allocationList, allocationListSize); | 
|  | memset(&(((char *)newAllocation)[allocationListSize]), 0, bytesPerPage); | 
|  |  | 
|  | allocationList = (Slot *)newAllocation; | 
|  | allocationListSize = newSize; | 
|  | slotCount += slotsPerPage; | 
|  | unUsedSlots += slotsPerPage; | 
|  |  | 
|  | free(oldAllocation); | 
|  |  | 
|  | /* | 
|  | * Keep access to the allocation list open at this point, because | 
|  | * I am returning to memalign(), which needs that access. | 
|  | */ | 
|  | noAllocationListProtection = 0; | 
|  | internalUse = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is the memory allocator. When asked to allocate a buffer, allocate | 
|  | * it in such a way that the end of the buffer is followed by an inaccessable | 
|  | * memory page. If software overruns that buffer, it will touch the bad page | 
|  | * and get an immediate segmentation fault. It's then easy to zero in on the | 
|  | * offending code with a debugger. | 
|  | * | 
|  | * There are a few complications. If the user asks for an odd-sized buffer, | 
|  | * we would have to have that buffer start on an odd address if the byte after | 
|  | * the end of the buffer was to be on the inaccessable page. Unfortunately, | 
|  | * there is lots of software that asks for odd-sized buffers and then | 
|  | * requires that the returned address be word-aligned, or the size of the | 
|  | * buffer be a multiple of the word size. An example are the string-processing | 
|  | * functions on Sun systems, which do word references to the string memory | 
|  | * and may refer to memory up to three bytes beyond the end of the string. | 
|  | * For this reason, I take the alignment requests to memalign() and valloc() | 
|  | * seriously, and | 
|  | * | 
|  | * Electric Fence wastes lots of memory. I do a best-fit allocator here | 
|  | * so that it won't waste even more. It's slow, but thrashing because your | 
|  | * working set is too big for a system's RAM is even slower. | 
|  | */ | 
|  | extern C_LINKAGE void *memalign(size_t alignment, size_t userSize) | 
|  | { | 
|  | register Slot *slot; | 
|  | register size_t count; | 
|  | Slot *fullSlot = 0; | 
|  | Slot *emptySlots[2]; | 
|  | size_t internalSize; | 
|  | size_t slack; | 
|  | char *address; | 
|  |  | 
|  | if (allocationList == 0) | 
|  | initialize(); | 
|  |  | 
|  | lock(); | 
|  | if (userSize == 0 && !EF_ALLOW_MALLOC_0) | 
|  | EF_Abort("Allocating 0 bytes, probably a bug."); | 
|  |  | 
|  | /* | 
|  | * If EF_PROTECT_BELOW is set, all addresses returned by malloc() | 
|  | * and company will be page-aligned. | 
|  | */ | 
|  | if (!EF_PROTECT_BELOW && alignment > 1) { | 
|  | if ((slack = userSize % alignment) != 0) | 
|  | userSize += alignment - slack; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The internal size of the buffer is rounded up to the next page-size | 
|  | * boudary, and then we add another page's worth of memory for the | 
|  | * dead page. | 
|  | */ | 
|  | internalSize = userSize + bytesPerPage; | 
|  | if ((slack = internalSize % bytesPerPage) != 0) | 
|  | internalSize += bytesPerPage - slack; | 
|  |  | 
|  | /* | 
|  | * These will hold the addresses of two empty Slot structures, that | 
|  | * can be used to hold information for any memory I create, and any | 
|  | * memory that I mark free. | 
|  | */ | 
|  | emptySlots[0] = 0; | 
|  | emptySlots[1] = 0; | 
|  |  | 
|  | /* | 
|  | * The internal memory used by the allocator is currently | 
|  | * inaccessable, so that errant programs won't scrawl on the | 
|  | * allocator's arena. I'll un-protect it here so that I can make | 
|  | * a new allocation. I'll re-protect it before I return. | 
|  | */ | 
|  | if (!noAllocationListProtection) | 
|  | Page_AllowAccess(allocationList, allocationListSize); | 
|  |  | 
|  | /* | 
|  | * If I'm running out of empty slots, create some more before | 
|  | * I don't have enough slots left to make an allocation. | 
|  | */ | 
|  | if (!internalUse && unUsedSlots < 7) { | 
|  | allocateMoreSlots(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Iterate through all of the slot structures. Attempt to find a slot | 
|  | * containing free memory of the exact right size. Accept a slot with | 
|  | * more memory than we want, if the exact right size is not available. | 
|  | * Find two slot structures that are not in use. We will need one if | 
|  | * we split a buffer into free and allocated parts, and the second if | 
|  | * we have to create new memory and mark it as free. | 
|  | * | 
|  | */ | 
|  |  | 
|  | for (slot = allocationList, count = slotCount; count > 0; count--) { | 
|  | if (slot->mode == FREE && slot->internalSize >= internalSize) { | 
|  | if (!fullSlot || slot->internalSize < | 
|  | fullSlot->internalSize) { | 
|  | fullSlot = slot; | 
|  | if (slot->internalSize == internalSize && | 
|  | emptySlots[0]) | 
|  | break; /* All done, */ | 
|  | } | 
|  | } else if (slot->mode == NOT_IN_USE) { | 
|  | if (!emptySlots[0]) | 
|  | emptySlots[0] = slot; | 
|  | else if (!emptySlots[1]) | 
|  | emptySlots[1] = slot; | 
|  | else if (fullSlot && fullSlot->internalSize == | 
|  | internalSize) | 
|  | break; /* All done. */ | 
|  | } | 
|  | slot++; | 
|  | } | 
|  | if (!emptySlots[0]) | 
|  | internalError(); | 
|  |  | 
|  | if (!fullSlot) { | 
|  | /* | 
|  | * I get here if I haven't been able to find a free buffer | 
|  | * with all of the memory I need. I'll have to create more | 
|  | * memory. I'll mark it all as free, and then split it into | 
|  | * free and allocated portions later. | 
|  | */ | 
|  | size_t chunkSize = MEMORY_CREATION_SIZE; | 
|  |  | 
|  | if (!emptySlots[1]) | 
|  | internalError(); | 
|  |  | 
|  | if (chunkSize < internalSize) | 
|  | chunkSize = internalSize; | 
|  |  | 
|  | if ((slack = chunkSize % bytesPerPage) != 0) | 
|  | chunkSize += bytesPerPage - slack; | 
|  |  | 
|  | /* Use up one of the empty slots to make the full slot. */ | 
|  | fullSlot = emptySlots[0]; | 
|  | emptySlots[0] = emptySlots[1]; | 
|  | fullSlot->internalAddress = Page_Create(chunkSize); | 
|  | fullSlot->internalSize = chunkSize; | 
|  | fullSlot->mode = FREE; | 
|  | unUsedSlots--; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If I'm allocating memory for the allocator's own data structures, | 
|  | * mark it INTERNAL_USE so that no errant software will be able to | 
|  | * free it. | 
|  | */ | 
|  | if (internalUse) | 
|  | fullSlot->mode = INTERNAL_USE; | 
|  | else | 
|  | fullSlot->mode = ALLOCATED; | 
|  |  | 
|  | /* | 
|  | * If the buffer I've found is larger than I need, split it into | 
|  | * an allocated buffer with the exact amount of memory I need, and | 
|  | * a free buffer containing the surplus memory. | 
|  | */ | 
|  | if (fullSlot->internalSize > internalSize) { | 
|  | emptySlots[0]->internalSize = | 
|  | fullSlot->internalSize - internalSize; | 
|  | emptySlots[0]->internalAddress = | 
|  | ((char *)fullSlot->internalAddress) + internalSize; | 
|  | emptySlots[0]->mode = FREE; | 
|  | fullSlot->internalSize = internalSize; | 
|  | unUsedSlots--; | 
|  | } | 
|  |  | 
|  | if (!EF_PROTECT_BELOW) { | 
|  | /* | 
|  | * Arrange the buffer so that it is followed by an inaccessable | 
|  | * memory page. A buffer overrun that touches that page will | 
|  | * cause a segmentation fault. | 
|  | */ | 
|  | address = (char *)fullSlot->internalAddress; | 
|  |  | 
|  | /* Set up the "live" page. */ | 
|  | if (internalSize - bytesPerPage > 0) | 
|  | Page_AllowAccess(fullSlot->internalAddress, | 
|  | internalSize - bytesPerPage); | 
|  |  | 
|  | address += internalSize - bytesPerPage; | 
|  |  | 
|  | /* Set up the "dead" page. */ | 
|  | Page_DenyAccess(address, bytesPerPage); | 
|  |  | 
|  | /* Figure out what address to give the user. */ | 
|  | address -= userSize; | 
|  | } else { /* EF_PROTECT_BELOW != 0 */ | 
|  | /* | 
|  | * Arrange the buffer so that it is preceded by an inaccessable | 
|  | * memory page. A buffer underrun that touches that page will | 
|  | * cause a segmentation fault. | 
|  | */ | 
|  | address = (char *)fullSlot->internalAddress; | 
|  |  | 
|  | /* Set up the "dead" page. */ | 
|  | Page_DenyAccess(address, bytesPerPage); | 
|  |  | 
|  | address += bytesPerPage; | 
|  |  | 
|  | /* Set up the "live" page. */ | 
|  | if (internalSize - bytesPerPage > 0) | 
|  | Page_AllowAccess(address, internalSize - bytesPerPage); | 
|  | } | 
|  |  | 
|  | fullSlot->userAddress = address; | 
|  | fullSlot->userSize = userSize; | 
|  |  | 
|  | /* | 
|  | * Make the pool's internal memory inaccessable, so that the program | 
|  | * being debugged can't stomp on it. | 
|  | */ | 
|  | if (!internalUse) | 
|  | Page_DenyAccess(allocationList, allocationListSize); | 
|  |  | 
|  | unlock(); | 
|  | return address; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Find the slot structure for a user address. | 
|  | */ | 
|  | static Slot *slotForUserAddress(void *address) | 
|  | { | 
|  | register Slot *slot = allocationList; | 
|  | register size_t count = slotCount; | 
|  |  | 
|  | for (; count > 0; count--) { | 
|  | if (slot->userAddress == address) | 
|  | return slot; | 
|  | slot++; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Find the slot structure for an internal address. | 
|  | */ | 
|  | static Slot *slotForInternalAddress(void *address) | 
|  | { | 
|  | register Slot *slot = allocationList; | 
|  | register size_t count = slotCount; | 
|  |  | 
|  | for (; count > 0; count--) { | 
|  | if (slot->internalAddress == address) | 
|  | return slot; | 
|  | slot++; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Given the internal address of a buffer, find the buffer immediately | 
|  | * before that buffer in the address space. This is used by free() to | 
|  | * coalesce two free buffers into one. | 
|  | */ | 
|  | static Slot *slotForInternalAddressPreviousTo(void *address) | 
|  | { | 
|  | register Slot *slot = allocationList; | 
|  | register size_t count = slotCount; | 
|  |  | 
|  | for (; count > 0; count--) { | 
|  | if (((char *)slot->internalAddress) + slot->internalSize == | 
|  | address) | 
|  | return slot; | 
|  | slot++; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE void free(void *address) | 
|  | { | 
|  | Slot *slot; | 
|  | Slot *previousSlot = 0; | 
|  | Slot *nextSlot = 0; | 
|  |  | 
|  | lock(); | 
|  |  | 
|  | if (address == 0) { | 
|  | unlock(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (allocationList == 0) | 
|  | EF_Abort("free() called before first malloc()."); | 
|  |  | 
|  | if (!noAllocationListProtection) | 
|  | Page_AllowAccess(allocationList, allocationListSize); | 
|  |  | 
|  | slot = slotForUserAddress(address); | 
|  |  | 
|  | if (!slot) | 
|  | EF_Abort("free(%a): address not from malloc().", address); | 
|  |  | 
|  | if (slot->mode != ALLOCATED) { | 
|  | if (internalUse && slot->mode == INTERNAL_USE) | 
|  | /* Do nothing. */; | 
|  | else { | 
|  | EF_Abort("free(%a): freeing free memory.", address); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (EF_PROTECT_FREE) | 
|  | slot->mode = PROTECTED; | 
|  | else | 
|  | slot->mode = FREE; | 
|  |  | 
|  | if (EF_FREE_WIPES) | 
|  | memset(slot->userAddress, 0xbd, slot->userSize); | 
|  |  | 
|  | previousSlot = slotForInternalAddressPreviousTo(slot->internalAddress); | 
|  | nextSlot = slotForInternalAddress(((char *)slot->internalAddress) + | 
|  | slot->internalSize); | 
|  |  | 
|  | if (previousSlot && | 
|  | (previousSlot->mode == FREE || previousSlot->mode == PROTECTED)) { | 
|  | /* Coalesce previous slot with this one. */ | 
|  | previousSlot->internalSize += slot->internalSize; | 
|  | if (EF_PROTECT_FREE) | 
|  | previousSlot->mode = PROTECTED; | 
|  |  | 
|  | slot->internalAddress = slot->userAddress = 0; | 
|  | slot->internalSize = slot->userSize = 0; | 
|  | slot->mode = NOT_IN_USE; | 
|  | slot = previousSlot; | 
|  | unUsedSlots++; | 
|  | } | 
|  | if (nextSlot && (nextSlot->mode == FREE || nextSlot->mode == PROTECTED)) | 
|  | { | 
|  | /* Coalesce next slot with this one. */ | 
|  | slot->internalSize += nextSlot->internalSize; | 
|  | nextSlot->internalAddress = nextSlot->userAddress = 0; | 
|  | nextSlot->internalSize = nextSlot->userSize = 0; | 
|  | nextSlot->mode = NOT_IN_USE; | 
|  | unUsedSlots++; | 
|  | } | 
|  |  | 
|  | slot->userAddress = slot->internalAddress; | 
|  | slot->userSize = slot->internalSize; | 
|  |  | 
|  | /* | 
|  | * Free memory is _always_ set to deny access. When EF_PROTECT_FREE | 
|  | * is true, free memory is never reallocated, so it remains access | 
|  | * denied for the life of the process. When EF_PROTECT_FREE is false, | 
|  | * the memory may be re-allocated, at which time access to it will be | 
|  | * allowed again. | 
|  | */ | 
|  | Page_DenyAccess(slot->internalAddress, slot->internalSize); | 
|  |  | 
|  | if (!noAllocationListProtection) | 
|  | Page_DenyAccess(allocationList, allocationListSize); | 
|  |  | 
|  | unlock(); | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE void *realloc(void *oldBuffer, size_t newSize) | 
|  | { | 
|  | void *newBuffer = malloc(newSize); | 
|  |  | 
|  | lock(); | 
|  |  | 
|  | if (oldBuffer) { | 
|  | size_t size; | 
|  | Slot *slot; | 
|  |  | 
|  | Page_AllowAccess(allocationList, allocationListSize); | 
|  | noAllocationListProtection = 1; | 
|  |  | 
|  | slot = slotForUserAddress(oldBuffer); | 
|  |  | 
|  | if (slot == 0) | 
|  | EF_Abort("realloc(%a, %d): address not from malloc().", | 
|  | oldBuffer, newSize); | 
|  |  | 
|  | if (newSize < (size = slot->userSize)) | 
|  | size = newSize; | 
|  |  | 
|  | if (size > 0) | 
|  | memcpy(newBuffer, oldBuffer, size); | 
|  |  | 
|  | free(oldBuffer); | 
|  | noAllocationListProtection = 0; | 
|  | Page_DenyAccess(allocationList, allocationListSize); | 
|  |  | 
|  | if (size < newSize) | 
|  | memset(&(((char *)newBuffer)[size]), 0, newSize - size); | 
|  |  | 
|  | /* Internal memory was re-protected in free() */ | 
|  | } | 
|  | unlock(); | 
|  |  | 
|  | return newBuffer; | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE void *malloc(size_t size) | 
|  | { | 
|  | if (!allocationList) | 
|  | initialize(); /* This sets EF_ALIGNMENT */ | 
|  | return memalign(EF_ALIGNMENT, size); | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE char *strdup(const char *s1) | 
|  | { | 
|  | if (!s1) | 
|  | return 0; | 
|  | char *s2 = malloc(strlen(s1) + 1); | 
|  |  | 
|  | if (!s2) { | 
|  | errno = ENOMEM; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return strcpy(s2, s1); | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE char *strndup(const char *s1, size_t n) | 
|  | { | 
|  | if (!s1) | 
|  | return 0; | 
|  | int complete_size = n; /* includes terminating null */ | 
|  | for (int i = 0; i < n - 1; i++) { | 
|  | if (!s1[i]) { | 
|  | complete_size = i + 2; | 
|  | break; | 
|  | } | 
|  | } | 
|  | char *s2 = malloc(complete_size); | 
|  |  | 
|  | if (!s2) { | 
|  | errno = ENOMEM; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | strncpy(s2, s1, complete_size - 1); | 
|  | s2[complete_size - 1] = '\0'; | 
|  |  | 
|  | return s2; | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE void *calloc(size_t nelem, size_t elsize) | 
|  | { | 
|  | size_t size = nelem * elsize; | 
|  | void *allocation; | 
|  |  | 
|  | allocation = malloc(size); | 
|  | memset(allocation, 0, size); | 
|  |  | 
|  | return allocation; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This will catch more bugs if you remove the page alignment, but it | 
|  | * will break some software. | 
|  | */ | 
|  | extern C_LINKAGE void *valloc(size_t size) | 
|  | { | 
|  | return memalign(bytesPerPage, size); | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE int posix_memalign(void **memptr, size_t alignment, | 
|  | size_t size) | 
|  | { | 
|  | *memptr = memalign(alignment, size); | 
|  | if (!*memptr) { | 
|  | errno = ENOMEM; | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | extern C_LINKAGE void *aligned_alloc(size_t alignment, size_t size) | 
|  | { | 
|  | return memalign(alignment, size); | 
|  | } |