Exemple #1
0
extern C_LINKAGE void DpsEfenceCheckLeaks(void) {
    register Slot *	slot = allocationList;
    register size_t	count = slotCount;

    if ( allocationList == 0 )
        EF_Abort("DpsEfenceCheckLeaks() called before first DpsMalloc().");

    lock();

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


    for ( ; count > 0; count-- ) {
        if ( slot->mode == ALLOCATED ) {
            fprintf(stderr, "Non-freed memory at 0x%x size:%d at %s:%d\n", slot->userAddress, slot->userSize, slot->filename, slot->fileline);
        }
        slot++;
    }


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

    release();

    return;
}
Exemple #2
0
extern C_LINKAGE void *
realloc(void * oldBuffer, size_t newSize)
{
	void *	newBuffer = 0;

	if ( allocationList == 0 )
		initialize();	/* This sets EF_ALIGNMENT */

        lock();

	newBuffer = memalign_locked(EF_ALIGNMENT, newSize);

	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_locked(oldBuffer);
		noAllocationListProtection = 0;
		Page_DenyAccess(allocationList, allocationListSize);

		if ( size < newSize )
			memset(&(((char *)newBuffer)[size]), 0, newSize - size);
		
		/* Internal memory was re-protected in free() */
	}
	release();

	return newBuffer;
}
Exemple #3
0
extern C_LINKAGE void * _DpsRealloc(void * oldBuffer, size_t newSize, const char *filename, size_t fileline) {
    void *	newBuffer = 0;

    if ( allocationList == 0 )
        initialize();	/* This sets EF_ALIGNMENT */
    /*
    fprintf(stderr, "DpsRealloc: %p at %s:%d\n", oldBuffer, filename, fileline);
    */
    lock();

    newBuffer = _DpsMalloc(newSize, filename, fileline);

    if ( oldBuffer ) {
        size_t	size;
        Slot *	slot;


        Page_AllowAccess(allocationList, allocationListSize);
        noAllocationListProtection = 1;

        slot = slotForUserAddress(oldBuffer);

        if ( slot == 0 )
            EF_Abort("DpsRealloc(%a, %d): address not from DpsMalloc() at %s:%d.", oldBuffer, newSize, filename, fileline);

        if ( newSize < (size = slot->userSize) )
            size = newSize;

        if ( size > 0 )
            dps_memcpy(newBuffer, oldBuffer, size); /* was: dps_memmove */

        DpsFree(oldBuffer);
        noAllocationListProtection = 0;
        Page_DenyAccess(allocationList, allocationListSize);

        if ( size < newSize )
            memset(&(((char *)newBuffer)[size]), 0, newSize - size);

        /* Internal memory was re-protected in free() */
    }

    release();

    return newBuffer;
}
extern C_LINKAGE void *
ef_realloc(void * oldBuffer, size_t newSize)
{
	void *	newBuffer = ef_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);

		ef_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;
}
Exemple #5
0
void
Page_Delete(void * address, size_t size)
{
	Page_DenyAccess(address, size);
}
Exemple #6
0
void
Page_Delete(void * address, size_t size)
{
	if ( munmap((caddr_t)address, size) < 0 )
		Page_DenyAccess(address, size);
}
Exemple #7
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");*/
}
Exemple #8
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;
}
Exemple #9
0
/*
 * 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;

    EF_Print(version);

#ifdef __linux__
    {
        struct rlimit nolimit = { RLIM_INFINITY, RLIM_INFINITY };
        int rc = setrlimit( RLIMIT_AS, &nolimit);
    }
#endif

    lock();

    /*
     * 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;
    }


    /*
     * Check if we should be filling new memory with a value.
     */
    if ( EF_FILL == -1 ) {
        if ( (string = getenv("EF_FILL")) != 0)
            EF_FILL = (unsigned char) atoi(string);
    }

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

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

    release();
#ifdef HAVE_PTHREAD
    if (!semEnabled) {
        semEnabled = 1;
#if USE_DPS_MUTEX
        InitMutex(&ef_mutex);
#else
        if (sem_init(&EF_sem, 0, 1) < 0) {
            semEnabled = 0;
        }
#endif
    }
#endif
}
Exemple #10
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);
}
extern C_LINKAGE void
ef_free(void * address)
{
	Slot *	slot;
	Slot *	previousSlot = 0;
	Slot *	nextSlot = 0;

    //printf(" ::free %p \n",address);
    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();
}