/* * 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; }
// load in an image from the filename, and store it in IPAGE. // if there were any errors, return a NULL IPAGE * iff_file_load_page( char * filename ) { int x,y; int plane; int handle_x = 0; int handle_y = 1; BLOCKHEADER bh; IFF_BMHD bmhd; FILE * fp; COLEL * palette = NULL; IPAGE * iff_page = NULL; fp = fopen(filename, "r"); // skip the container header... (void)endian_little_read_32(fp); (void)endian_little_read_32(fp); (void)endian_little_read_32(fp); bmhd.width=0; bmhd.height=0; // parse the file... iff_read_header(fp, &bh); while (bh.size) { /* printf( "%c%c%c%c\n", (bh.name>>24)&0xff, (bh.name>>16)&0xff, (bh.name>>8)&0xff, (bh.name)&0xff ); */ switch (bh.name) { case(IFF_BLOCK_BMHD): //printf("Bitmap header\n"); bmhd.width = endian_little_read_16(fp) & 0x00ffff; bmhd.height = endian_little_read_16(fp) & 0x00ffff; bmhd.x = endian_little_read_16(fp) & 0x00ffff; bmhd.y = endian_little_read_16(fp) & 0x00ffff; bmhd.planes = endian_little_read_8(fp) & 0x0000ff; bmhd.masking = endian_little_read_8(fp) & 0x0000ff; bmhd.comp = endian_little_read_8(fp) & 0x0000ff; (void)endian_little_read_8(fp); // pad. not used. -- store 0 bmhd.xparent = endian_little_read_16(fp) & 0x00ffff; bmhd.xaspect = endian_little_read_8(fp) & 0x0000ff; bmhd.yaspect = endian_little_read_8(fp) & 0x0000ff; bmhd.pwidth = endian_little_read_16(fp) & 0x00ffff; bmhd.pheight = endian_little_read_16(fp) & 0x00ffff; /* printf("image is %d by %d (%d %d) (%d %d)\n", bmhd.width, bmhd.height, bmhd.pwidth, bmhd.pheight, bmhd.xaspect, bmhd.yaspect); printf("%d planes %d %s\n", bmhd.planes, bmhd.masking, (bmhd.comp==1)?"RLE":"?"); */ break; case(IFF_BLOCK_GRAB): handle_x = endian_little_read_16(fp); handle_y = endian_little_read_16(fp); break; case(IFF_BLOCK_CMAP): palette = (COLEL *) malloc(bh.size*sizeof(COLEL)); for (x=0 ; x<bh.size/3 ; x++) { palette[x].r = endian_little_read_8(fp)&0x00ff; palette[x].g = endian_little_read_8(fp)&0x00ff; palette[x].b = endian_little_read_8(fp)&0x00ff; } break; case(IFF_BLOCK_CRNG): // Electronic Arts Deluxe Paint color range (void)endian_little_read_16(fp); // pad -- store 0 (void)endian_little_read_16(fp); // rate (void)endian_little_read_16(fp); // active -- 1 means cycle them (void)endian_little_read_8(fp); // start range (void)endian_little_read_8(fp); // end range break; case(IFF_BLOCK_CCRT): // Commodore Graphicraft color range (void)endian_little_read_16(fp); // direction 0=stop 1=forw -1=back (void)endian_little_read_8(fp); // start range (void)endian_little_read_8(fp); // end range (void)endian_little_read_32(fp); // seconds (void)endian_little_read_32(fp); // microseconds (void)endian_little_read_16(fp); // pad -- store 0 break; case(IFF_BLOCK_BODY): //printf("Image Body %d (%d colors)\n", bh.size, 1<<bmhd.planes); iff_page = Page_Create(bmhd.width, bmhd.height, 1<<bmhd.planes); iff_page->handle_x = handle_x; iff_page->handle_y = handle_y; // at this point, we should have a palette, but just in case, // we'll put an if around this anyway... if (palette) { free(iff_page->palette); iff_page->palette = palette; } // now load in the bitmap data... for (y=0 ; y < iff_page->h ; y++) { // decode image data for ( plane = 0 ; plane < bmhd.planes ; plane++) { if (1) //bmhd.comp==1) { iff_decode_plane(fp, iff_page->pimage, y, iff_page->w, plane); } else { iff_load_in_plane(fp, iff_page->pimage, y, iff_page->w, plane); } } // and the mask... if (bmhd.masking == 1) { iff_decode_plane(fp, iff_page->alpha, y, iff_page->w, 1); } } return iff_page; break; default: iff_skip_block(fp, &bh); } iff_read_header(fp, &bh); } fclose(fp); return iff_page; }
/* * 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 }