Example #1
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.
 */
static void * _DpsMemalign(size_t alignment, size_t userSize, const char *filename, size_t fileline) {
    register Slot   *slot, *slot2;
    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 && strcmp(filename, "efence.c"))
        EF_Abort("Allocating 0 bytes, probably a bug at %s:%d.", filename, fileline);

    /*
     * 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.
     *
     */
    slot = allocationList;
    slot2 = &slot[slotCount - 1];
    while (slot <= slot2) {
        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. */
        }

        if ( slot2->mode == FREE
                && slot2->internalSize >= internalSize ) {
            if ( !fullSlot
                    ||slot2->internalSize < fullSlot->internalSize) {
                fullSlot = slot2;
                if ( slot2->internalSize == internalSize
                        && emptySlots[0] )
                    break;	/* All done, */
            }
        }
        else if ( slot2->mode == NOT_IN_USE ) {
            if ( !emptySlots[0] )
                emptySlots[0] = slot2;
            else if ( !emptySlots[1] )
                emptySlots[1] = slot2;
            else if ( fullSlot
                      && fullSlot->internalSize == internalSize )
                break;	/* All done. */
        }
        slot++;
        slot2--;
    }

    /*
    	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] )
        EF_InternalError("No empty slot 0.");

    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] )
            EF_InternalError("No empty slot 1.");

        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--;

        /* Fill the slot if it was specified to do so. */
        if ( EF_FILL != -1 )
            memset(
                (char *)fullSlot->internalAddress
                ,EF_FILL
                ,chunkSize);
    }

    /*
     * 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. */
        if ( EF_PROTECT_FREE )
            Page_Delete(address, bytesPerPage);
        else
            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. */
        if ( EF_PROTECT_FREE )
            Page_Delete(address, bytesPerPage);
        else
            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;
    fullSlot->fileline = fileline;
    dps_strncpy(fullSlot->filename, filename, DPS_FILENAMELEN);

    /*	if (slotCount > 1) DpsSort(allocationList, slotCount, sizeof(Slot), (qsort_cmp)cmp_Slot);*/

    /*
     * Make the pool's internal memory inaccessable, so that the program
     * being debugged can't stomp on it.
     */
    if ( !internalUse )
        Page_DenyAccess(allocationList, allocationListSize);

    release();

    /*	if (address == 0x292d3000) {
    	  int r = 1 / 0;
    	  printf("Error r:%d\n");
    	}*/

    /*
    	fprintf(stderr, " -- allocated: %p @ %s:%d\n", address, filename, fileline);
    */
    return address;
}
Example #2
0
extern C_LINKAGE void _DpsFree(void * address, const char *filename, size_t fileline) {
    Slot *	slot;
    Slot *	previousSlot = 0;
    Slot *	nextSlot = 0;

    if ( address == 0 )
        return;
    /*
    fprintf(stderr, "DpsFree: %p at %s:%d\n", address, filename, fileline);
    */
    if ( allocationList == 0 )
        EF_Abort("DpsFree() called before first DpsMalloc() at %s:%d.", filename, fileline);

    lock();

    if ( !noAllocationListProtection )
        Page_AllowAccess(allocationList, allocationListSize);

    slot = slotForUserAddress(address);

    if ( !slot ) {
        /*	  EF_Abort("DpsFree(%a): address not from DpsMalloc() at %s:%d.", address, filename, fileline);*/
        EF_Print("DpsFree(%a): address not from DpsMalloc() at %s:%d.\n", address, filename, fileline);
        release();
        return;
    }
    /*
    EF_Print("DpsFree(%a): slot=%a\n", address, slot);
    */

    if ( slot->mode != ALLOCATED ) {
        if ( internalUse && slot->mode == INTERNAL_USE )
            /* Do nothing. */;
        else {
            if (slot->mode == FREE) EF_Print("DpsFree(%a) FREE\n", address);
            if (slot->mode == PROTECTED) EF_Print("DpsFree(%a) PROTECTED\n", address);
            if (slot->mode == INTERNAL_USE) EF_Print("DpsFree(%a) INTERNAL_USE\n", address);
            if (slot->mode == NOT_IN_USE) EF_Print("DpsFree(%a) NOT_IN_USE\n", address);
            EF_Abort("DpsFree(%a): freeing free memory at %s:%d.", address, filename, fileline);
        }
    }

    if ( EF_PROTECT_FREE )
        slot->mode = PROTECTED;
    else
        slot->mode = FREE;

    /*
     * 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.
     *
     * Some operating systems allow munmap() with single-page resolution,
     * and allow you to un-map portions of a region, rather than the
     * entire region that was mapped with mmap(). On those operating
     * systems, we can release protected free pages with Page_Delete(),
     * in the hope that the swap space attached to those pages will be
     * released as well.
     */
    if ( EF_PROTECT_FREE )
        Page_Delete(slot->internalAddress, slot->internalSize);
    else
        Page_DenyAccess(slot->internalAddress, slot->internalSize);

    previousSlot = slotForInternalAddressPreviousTo(slot->internalAddress);
    nextSlot = slotForInternalAddress(
                   ((char *)slot->internalAddress) + slot->internalSize);

    if ( previousSlot && (previousSlot->mode == slot->mode) ) {
        /* Coalesce previous slot with this one. */
        previousSlot->internalSize += slot->internalSize;
        slot->internalAddress = slot->userAddress = 0;
        slot->internalSize = slot->userSize = 0;
        slot->mode = NOT_IN_USE;
        slot = previousSlot;
        unUsedSlots++;
    }
    if ( nextSlot && (nextSlot->mode == slot->mode) ) {
        /* 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;
    */

    if ( !noAllocationListProtection )
        Page_DenyAccess(allocationList, allocationListSize);

    release();

    /*	fprintf(stderr, "DpsFree Done\n");*/
}
Example #3
0
static void
free_locked(void * address)
{
	Slot *	slot;
	Slot *	previousSlot = 0;
	Slot *	nextSlot = 0;

	if ( address == 0 )
		return;

	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;

	/*
	 * 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.
	 *
	 * Some operating systems allow munmap() with single-page resolution,
	 * and allow you to un-map portions of a region, rather than the
	 * entire region that was mapped with mmap(). On those operating
	 * systems, we can release protected free pages with Page_Delete(),
	 * in the hope that the swap space attached to those pages will be
	 * released as well.
	 */
	Page_Delete(slot->internalAddress, slot->internalSize);

	previousSlot = slotForInternalAddressPreviousTo(slot->internalAddress);
	nextSlot = slotForInternalAddress(
	 ((char *)slot->internalAddress) + slot->internalSize);

	if ( previousSlot && previousSlot->mode == slot->mode ) {
		/* Coalesce previous slot with this one. */
		previousSlot->internalSize += slot->internalSize;
		slot->internalAddress = slot->userAddress = 0;
		slot->internalSize = slot->userSize = 0;
		slot->mode = NOT_IN_USE;
		slot = previousSlot;
		unUsedSlots++;
	}
	if ( nextSlot && nextSlot->mode == slot->mode ) {
		/* 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;

	if ( !noAllocationListProtection )
		Page_DenyAccess(allocationList, allocationListSize);
}