Fortify - A fortified memory allocation shell for C and C++ ----------------------------------------------------------- written by Simon P. Bullen Version 2.2, 1 November 1995 This software is not public domain. All material in this archive is (C) Copyright 1995 Simon P. Bullen. The software is freely distributable, with the condition that no more than a nominal fee is charged for media. Everything in this distribution must be kept together, in original, unmodified form. The software may be modified for your own personal use, but modified files may not be distributed. The material is provided "as is" without warranty of any kind. The author accepts no responsibility for damage caused by this software. This software may not be used in any way by Microsoft Corporation or its subsidiaries, or current employees of Microsoft Corporation or its subsidiaries. This software may not be used for the construction, development, production, or testing of weapon systems of any kind. This software may not be used for the construction, development, production, or use of plants/installations which include the processing of radioactive/fissionable material. If you use this software at all, I'd love to hear from you. All questions, criticisms, suggestions, praise and postcards are most welcome. email: sbullen@cybergraphic.com.au snail: Simon P. Bullen PO BOX 12138 A'Beckett St. Melbourne 3000 Australia CONTENTS -------- Your archive should have the following files: fortify.doc - This file. Read it first fortify.cxx - Actual code. Rename to fortify.c for C only development fortify.h - Each file in your program should #include this ufortify.h - User configuration file. Here is where you set your options test.c - C tester test2.cxx - C++ tester makefile - Makefile for GCC to build the testers. OVERVIEW -------- Fortify is a (fortified) shell for memory allocations. It supports malloc/calloc/realloc/strdup/free and new/delete. It traps memory leaks, writes beyond and before memory blocks, writes to freed memory, free twice, freeing memory never allocated, and removes all randomness from reading uninitialized or free memory. It is a descendant of a library I wrote way back in 1990 called SafeMem. It can be adapted to most memory management functions; the original version of SafeMem worked only with the Amiga's AllocMem/FreeMem. Fortify works by allocating extra space on each block. This includes a private header (which is used to keep track of Fortify's list of allocated memory), and two "fortification zones" or "sentinels" (which are used to detect writing outside the bound of the user's memory). PORTABILITY ----------- Fortify is intended to be a highly portable tool. It should work quite happily in any vaguely ANSI C/C++ environment, on any operating system or processor architecture without modification. It does not, however, support bizarre memory models. A pointer is a pointer is a pointer. I have no intention of ever supporting any of the non-flat memory models used by some pretend operating systems. Fortify INSTALLATION (C++) -------------------------- To install Fortify, each source file will need to #include "fortify.h". To enable Fortify, define the symbol FORTIFY. The makefile is the best place to do this. The symbol FORTIFY will need to be defined for every module, this includes "fortify.c" or "fortify.cxx". If FORTIFY is not defined, it will compile away to nothing - it will be as if Fortify was never installed at all (you should recompile all sourcecode with Fortify disabled for a release of your software). If you do not have stdout available, you may wish to set an alternative output function. See Fortify_SetOutputFunc() and FORTIFY_AUTOMATIC_LOGFILE, below. Your program will need to link in "fortify.cxx" (after it's been compiled, of course). You may also need to adjust some options in "ufortify.h" before Fortify will work correctly. See "Compile Time Customizations", below. Fortify INSTALLATION (C) ------------------------ To install Fortify into a C program, simply rename "fortify.cxx" to "fortify.c", and follow the C++ instructions above. "fortify.cxx" will compile quite happily as C, as all the core Fortify code is C, and the C++ specific code is surrounded by #ifdef __cplusplus/#endif. You may also need to adjust some options in "ufortify.h" before Fortify will work correctly. See "Compile Time Customizations", below. COMPILE TIME CUSTOMIZATIONS --------------------------- The file "ufortify.h" contains many #defines that you can use to customize Fortify's behavior. Fortify will do it's best to provide sensible defaults for most of these options, if for some reason they are not present in "ufortify.h". Most of the options are merely user preferences that you may wish to change or turn on only when you are trying to track down a particularly nasty bug. There are a couple of configuration items, however, that will need to be set correctly for Fortify to compile on your particular system. When it can, Fortify will detect a compiler and set the appropriate options. Ideally, Fortify would be able to be built under all compilers without modification, but due to differences in compilers and the devious nature of some of the stuff Fortify has to acheive, this is sadly not possible. It is my aim to make Fortify automatically configure itself for all major compilers out there, so if you need to adjust some of these compiler-specific options to get Fortify to work under your system, I would greatly appreciate the code fragment that detects your compiler and sets up the correct options (these live in "fortify.h" - search for __GNUG__ for an example). #define FORTIFY_PROVIDE_ARRAY_NEW #define FORTIFY_PROVIDE_ARRAY_DELETE Some C++ compilers have separate operators for newing and deleting arrays. If your compiler does, you will need to define this symbol. If you are unsure, turn them both on. Your program won't compile or link if they should be off (providing your test code actually news and deletes an array). GCC 2.6.3 and Borland C++ 4.5 both need these symbols. Microsoft C++ 1.5 and SAS 6.5 C++ both don't. Fortify will automatically turn on this option for GCC and Borland.. If both array new and array delete are enabled, Fortify will be able to ensure that you always use the correct form of delete, and will issue an error message if you use the wrong one. Fortify: Incorrect deallocator "delete[]" detected at test2.cxx.17 (0x7438ac2,1,test2.cxx.13) was allocated with "new" #define FORTIFY_STRDUP strdup() is a non-ANSI function that is essentially a malloc() and a strcpy(). If you use this function, you will need to enable FORTIFY_STRDUP, so that the memory you get back from strdup() will be allocated by Fortify. If you don't do this, Fortify will generate an error message when you attempt to free the memory. #define FORTIFY_NO_PERCENT_P Some non-ANSI versions of sprintf() do not recognize "%p" as a valid conversion specification. Fortify uses "%p" to output all of it's pointers. If your sprintf() does not support "%p", define FORTIFY_NO_PERCENT_P, and Fortify will use "%lx" instead. #define FORTIFY_STORAGE You can use this to apply a storage type to all Fortify's exportable functions. If you are putting Fortify in an export library for example, you may need to put __export here, for example. #define FORTIFY_BEFORE_SIZE 32 #define FORTIFY_BEFORE_VALUE 0xA3 #define FORTIFY_AFTER_SIZE 32 #define FORTIFY_AFTER_VALUE 0xA5 These values define how much "fortification" is placed around each memory block you allocate. Fortify will place FORTIFY_BEFORE_SIZE bytes worth of memory right before your memory block, and FORTIFY_AFTER_SIZE bytes after your memory block, and these will be initialized to FORTIFY_BEFORE_VALUE and FORTIFY_AFTER_VALUE respectively. If your program then accidentally writes into either of these regions, Fortify will detect this, and issue an error message. If you don't want these fortifications to be allocated, specify a size of 0. Note that the value parameters are 8 bits. Fortify: Underwrite detected before block (0x743b462,123,test.c.14) at test.c.16 Memory integrity was last verified at test.c.14 Address Offset Data (a3) ...64 bytes skipped... 0x743b45a 64 a3a3a3a3 a3a3a341 "£££££££A" #define FORTIFY_ALIGNMENT sizeof(double) Many processors require data objects to have a specific alignment in memory. The memory allocators on these machines will only return memory blocks aligned correctly for the largest of these requirements. Fortify, however, adds some magic cookies to the start of it's memory blocks. Fortify will guarantee that the amount of memory it adds to the front of a memory block will be a multiple of FORTIFY_ALIGNMENT (It does this by increasing the FORTIFY_BEFORE_SIZE, after taking into account the size of it's private header). The default of sizeof(double) should work on most systems. #define FORTIFY_FILL_ON_ALLOCATE #define FORTIFY_FILL_ON_ALLOCATE_VALUE 0xA7 Programs often rely on uninitialized memory being certain values (usually 0). If you define FORTIFY_FILL_ON_ALLOCATE, all memory that you allocate will be initialized to FORTIFY_FILL_ON_ALLOCATE_VALUE, which you should define to be some horrid value (definitely NOT 0). This will encourage all code that relies on uninitialized memory to behave rather differently when Fortify is running. If you ever see a pointer that is 0xA7A7A7A7, you can be certain that it is uninitialized. #define FORTIFY_FILL_ON_DEALLOCATE #define FORTIFY_FILL_ON_DEALLOCATE_VALUE 0xA9 Programmers often try to use memory after they've freed it, which can sometimes work (so long as nobody else has modified the memory before you look at it), but is incredibly dangerous and definitely bad practice. If FORTIFY_FILL_ON_DEALLOCATE is defined, all memory you free will be stomped out with FORTIFY_FILL_ON_DEALLOCATE_VALUE, which ensures that any attempt to read freed memory will give incorrect results. If you ever see a pointer that is 0xA9A9A9A9, it definately came from free'd memory. #define FORTIFY_FILL_ON_CORRUPTION Fortify will never free memory that has had it's fortifications overwritten. This is so that you have ample opportunity to examine this memory in a debugger. If the memory remains corrupted, Fortify will report this each time it does a check of memory, which can be annoying (but useful, if the program continues to modify the memory). If you define FORTIFY_FILL_ON_CORRUPTION, Fortify will re-initialize a corrupted fortification after it issues it's message. Fortify will then no longer complain about this memory, until it is corrupted again. This has the extra advantage that software that tries to read this memory will probably break. #define FORTIFY_CHECK_ALL_MEMORY_ON_ALLOCATE #define FORTIFY_CHECK_ALL_MEMORY_ON_DEALLOCATE CHECK_ALL_MEMORY_ON... means that for every single memory allocation or deallocation, every single block of memory will be checked. This can slow down programs considerably if you have a large number of blocks allocated. You would normally only need to turn this on if you are trying to pinpoint where a corruption was occurring. A block of memory is always checked when it is freed, so if CHECK_ALL... isn't turned on, corruptions will still be detected, eventually. You can also force Fortify to check all memory with a call to Fortify_CheckAllMemory(). If you have a memory corruption you can't find, sprinkling these through the suspect code will help narrow it down. When you get a message regarding a corruption, part of the output will be a line that says "Memory integrity was last verified at file.line". This corresponds to when the "CheckAllMemory" operation was last successfully performed, either automatically, or by an explicit call to Fortify_CheckAllMemory(). #define FORTIFY_PARANOID_DEALLOCATE FORTIFY_PARANOID_DEALLOCATE causes Fortify to traverse the memory list to ensure the memory you are about to free was really allocated. This option is recommended in a protected memory operating system. If a free() of a garbage pointer is attempted, Fortify will generate a segmentation fault when it attempts to checksum the memory pointed to by the garbage pointer. If, however, FORTIFY_PARANOID_DELETE is on, Fortify will be able to determine that the pointer is garbage without crashing the system, and will be able to generate an error message. Note that in C++, however, if you try to "delete" a garbage pointer, you will most likely crash before Fortify gets a look in. When you do a "delete" in C++, the first thing that happens is the object's destructor gets called. Depending on what the destructor actually does, it may crash due to having a bogus "this". The memory isn't freed until AFTER the destructor, which is when Fortify would be able to detect the invalid pointer (too late). If you are having problems with a weird crash in a destructor, you may wish to add Fortify_CheckPointer(this) to the start of the destructor, but be warned that Fortify will generate an error message if you destruct an object that was created on the stack. FORTIFY_PARANOID_DEALLOCATE adds a small amount of overhead to freeing memory (though not nearly as much as FORTIFY_CHECK_ALL_MEMORY_ON_DEALLOCATE). Paranoid mode would be quicker if Fortify didn't use a naive linked list to store it's memory on (see TO DO LIST, below). #define FORTIFY_WARN_ON_ZERO_MALLOC #define FORTIFY_FAIL_ON_ZERO_MALLOC In some implementations, a malloc(0) will always fail (This is non-ANSI). If Fortify is turned on, malloc(0) won't actually reach malloc as a malloc(0) - Fortify adds fortifications, which means it won't be a zero byte allocation anymore, and will probably succeed. Thus it is possible to write code that would work if Fortify was turned on, but fail when Fortify is turned off. If the version of malloc() you are using does not allow malloc(0) to succeed, you must enable FORTIFY_FAIL_ON_ZERO_MALLOC to enable your program to function properly with Fortify turned on. #define FORTIFY_WARN_ON_ALLOCATE_FAIL This causes a debug to be issued whenever an allocation fails. This can be very useful when identifying the cause for a crash. #define FORTIFY_WARN_ON_FALSE_FAIL A debug will be issued when an allocation is "false failed". See Fortify_SetAllocateFailRate() and Fortify_SetAllocationLimit() for more information. Fortify: A "malloc()" of 128 bytes "false failed" at test.c.27 #define FORTIFY_WARN_ON_SIZE_T_OVERFLOW This causes Fortify to check for breaking the size_t limit. This is a problem in 16-bit applications where breaking the 16 bit limit is reasonably likely. The problem is that Fortify adds a small amount of overhead to a memory block; so in a 16-bit size_t environment, if you tried to allocate 64K, Fortify would make that block bigger than 64K and your allocation would fail due to the presence of Fortify. With size_t being 32 bits for all environments worth programming in, this problem is extremely unlikely (Unless you plan to allocate 4 gigabytes). #define FORTIFY_AUTOMATIC_LOG_FILE #define FORTIFY_LOG_FILENAME "fortify.log" #define FORTIFY_FIRST_ERROR_FUNCTION cout << "\a\a\aOh Dear!\n" If FORTIFY_AUTOMATIC_LOG_FILE is defined (C++ only), then Fortify will output to a log file. It will also automatically call Fortify_EnterScope() at program initialization, and Fortify_LeaveScope() at program termination. If Fortify generated no output, the log file will not be altered. FORTIFY_FIRST_ERROR_FUNCTION will be called upon generation of the first Fortify message, so that the user can tell if a Fortify report has been generated. Otherwise, Fortify would quietly write all this useful stuff out to the log file, and no-one would know to look there! You don't need to use Fortify_SetOutputFunc() at all if you're using the automatic log file. #define FORTIFY_LOCK() #define FORTIFY_UNLOCK() In a multi-threaded environment, we need to arbitrate access to the fortify memory list. This is what FORTIFY_LOCK() and FORTIFY_UNLOCK() are used for. The calls to FORTIFY_LOCK() and FORTIFY_UNLOCK() must nest. If no two threads/tasks/processes will be using the same Fortify at the same time, then FORTIFY_LOCK() and FORTIFY_UNLOCK() can safely be #defined away to nothing. #define FORTIFY_TRACK_DEALLOCATED_MEMORY #define FORTIFY_DEALLOCATED_MEMORY_LIMIT 1048576 #define FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY #define FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY If FORTIFY_TRACK_DEALLOCATED_MEMORY is enabled, Fortify won't actually free memory when you deallocate it. Freed memory goes onto the "deallocated" list, where Fortify will keep an eye on it. If you write to the memory after it's been freed, Fortify will be able to tell. If you attempt to free memory twice, Fortify will be able to tell you for certain, rather than just suspect. Deallocated memory tracking is only active if you've entered at least one level of Fortify scope. This is so that Fortify will be able to actually free the deallocated memory when the scope is left. Fortify: Write to deallocated block (0x75c4502,126,test.c.21) detected at test.c.40 Memory block was deallocated by "realloc()" at test.c.34 Memory integrity was last verified at test.c.35 Address Offset Data (a9) 0x75c4502 0 58a9a9a9 a9a9a9a9 a9a9a9a9 a9a9a9a9 "X" ...110 bytes skipped... FORTIFY_DEALLOCATED_MEMORY_LIMIT, if defined, is the maximum amount of memory that Fortify should keep on it's deallocated list. If you deallocate some memory, and the total amount of deallocated memory being tracked is greater than this number, then Fortify will throw away (actually free) the oldest chunks of memory, until it's under this threshold. You don't have to worry about running out of memory if this value is set high, Fortify will discard deallocated memory in preference to letting an allocation fail. The main reason for this option is to restrict the amount of time spent checking freed memory for corruption. FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY will cause Fortify to issue a warning whenever it has to free some deallocated memory when it didn't really want to. If an allocation was going to fail, or if it was over the FORTIFY_DEALLOCATED_MEMORY_LIMIT, and it had to free some memory, then a warning would be issued. Freeing deallocated memory when leaving a Fortify scope does not cause a warning. FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY is useful when you are trying to track down a particular write to some deallocated memory. Fortify will list the details of all blocks being discarded, so that you can tell if the block you are interested in is still being watched by Fortify. Note that Fortify will always perform one last check on the memory before it discards it, so you can be sure that all memory being discarded is corruption free. Fortify: Warning - Discarding deallocated memory at test.c.46 (0x743e8b2,126,test.c.22,test.c.35) (0x743f0b2,456,test.c.32,test.c.46) #define FORTIFY_DELETE_STACK_SIZE 256 Due to the way that delete works, Fortify has to maintain it's own stack of delete source-code information. For simplicity, this has been implemented in a static array. The size of this stack reflects the number of levels of recursion that Fortify will be able to track during deletes (ie deletes being called from destructors called from a delete call). If this recursion limit is exceeded, Fortify will simply output the deepest sourcecode level known. I think 256 levels should be ample. typedef void (*Fortify_NewHandlerFunc)(void); #define FORTIFY_NEW_HANDLER_FUNC Fortify_NewHandlerFunc By default, Fortify will use the standard new handler function prototype. However, some non-standard compiler implementations (such as Microsoft Visual C++ 2.0) use non-standard prototypes for their new handlers. FORTIFY_NEW_HANDLER_FUNC is the type that Fortify uses for it's new handler function pointer. Note that Fortify won't pass any parameters to the new handler, so changing this is probably quite useless at this point in time. RUN TIME CONTROL ---------------- Fortify can also be controlled at run time with a few special functions, which compile away to nothing if FORTIFY isn't defined. Fortify_Disable() - Fortify can be disabled at run-time. Previously this would only work if no memory was allocated at the time Fortify_Disable() was called, but in C++ environments, this is almost never possible. After Fortify_Disable() has been called, all new allocations will not be Fortified. Deallocations will be treated slightly differently. Fortify will check to see if it allocated the memory, and if so, free it. If not, it will pass it on to free(). Fortify_Disable() is useful to turn off Fortify if the overhead or bugs being induced by Fortify are getting in the way. To correctly remove Fortify for software release, you must compile it out. Note that once you've called Fortify_Disable(), there is no way to turn it back on again. Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output) - Sets the function used to output all error and diagnostic messages by Fortify. The output function takes a single (const char *) argument, and must be able to handle newlines. The default output function is a printf() to stdout, unless you are using FORTIFY_AUTOMATIC_LOG_FILE, where the default is to output to the log file. The function returns the old pointer. Fortify_SetAllocateFailRate(int Percent) - Fortify will make an allocation attempt "fail" this "Percent" of the time, even if the memory IS available. Useful to "stress-test" an application. Returns the old value. The fail rate defaults to 0. Fortify_SetAllocationLimit(unsigned long AllocationLimit) - Impose a limit on the amount of memory the application can allocate. This limit may be changed at any time. If you set this to 0, memory allocations are guaranteed to fail. The default is 0xffffffff, which is essentially no limit. Returns the old value. Fortify_GetCurrentAllocation() can be used to find out how much memory is currently allocated (see STATISTICAL FUNCTIONS, below). DIAGNOSTIC FUNCTIONS -------------------- Fortify also provides some additional diagnostic functions which can be used to track down memory corruption and memory leaks. If Fortify is disabled, these functions do nothing. If calling these functions directly from a debugger, remember to add the "const char *file" and "unsigned long line" parameters to each of the calls (these are normally added by the preprocessor macros). Fortify_CheckPointer(void *uptr) - Returns true if the uptr points to a valid block of allocated memory. The memory must be on Fortify's list, and it's sentinels must be in tact. If anything is wrong, an error message is issued (if Fortify is disabled, this function always returns true). Fortify_LabelPointer(void *uptr, const char *label) - Labels the memory block with a string provided by the user. This function takes a copy of the passed in string. The pointer MUST be one returned by a Fortify allocation function. This function can be very useful to track a memory block that was allocated on the same line as a bunch of other memory blocks. Whenever Fortify outputs the information about this memory block, the label string will also be output. Fortify_CheckAllMemory() - Checks the sentinels of all memory that Fortify knows about. This includes allocated memory, and if FORTIFY_TRACK_DEALLOCATED_MEMORY is turned on, any deallocated memory that Fortify is still tracking. Returns the number of blocks that failed (if Fortify is disabled, this function always returns 0). Fortify_ListAllMemory() - Outputs the entire list of currently allocated memory. For each block is output it's Address, Size, and the SourceFile and Line that allocated it. If there is no memory on the list, this function outputs nothing. It returns the number of blocks on the list (if Fortify has been disabled, this function always returns 0). Fortify_DumpAllMemory() - Outputs a hex dump of all currently allocated memory. Fortify_EnterScope() - enters a level of fortify scope. Returns the new scope level. Fortify_LeaveScope() - leaves a level of fortify scope, it also prints a dump of all unfreed memory allocated within the scope being left. This can be very useful in tracking down memory leaks in a part of a program. If you place a EnterScope/LeaveScope pair around a set of functions that should have no memory allocated when it's done, Fortify will let you know if this isn't the case. Fortify will also discard any deallocated memory from the scope being left, if it is tracking deallocated memory. STATISTICAL FUNCTIONS --------------------- Fortify gathers some basic statistics about your programs memory usage. It will keep track of the total number of memory allocation operations performed, and also the maximum amount of memory allocated at any one time. Fortify_OutputStatistics() - display the current statistics. Fortify: Statistics at test.c.70 Maximum memory allocated at one time: 13633 bytes in 7 blocks There have been 9 allocations and 4 deallocations There was a total of 14343 bytes allocated The average allocation was 1593 bytes Fortify_GetCurrentAllocation() - returns the total amount of memory currently allocated by the application (if Fortify is disabled, this function will always return 0). PROBLEMS WITH THE new AND delete MACROS --------------------------------------- Due to limitations of the C preprocessor, getting caller source-code information to new and delete isn't as easy as it is for malloc() and free(). The macro for "new" which adds this information onto the new call causes syntax errors if you try to declare a custom new operator. The actual Fortifying works fine, it's just the macro expansion which causes problems. If this happens, the easiest thing to do is #ifdef out the custom new/delete operators if FORTIFY is defined. Alternatively, you can not #include "fortify.h" for the offending file (and any files that use these custom operators, but remember that they won't have source-code information). eg. #ifndef FORTIFY /* can't easily use custom new's with FORTIFY enabled */ void *X::operator new(size_t size) { return malloc(size); } #endif Due to a limitation with delete (it is illegal to declare a version of delete that takes different parameters), Fortify has limited information about where delete is being called from, and so the the line and source information will often say "delete.0". If a delete is occurring from within another delete, Fortify is now able to maintain a 'delete stack', and will output this stack after the main body of the Fortify hit. WHEN TO USE FORTIFY ------------------- You should never be without Fortify when you're developing software. It will detect your bugs _as_you_write_them_, which makes them a lot easier to find. Leave Fortify enabled until the final test and release of your software. You probably won't want some of the slower options, such as CHECK_ALL_MEMORY_ON_DEALLOCATE. With the exception of those options, Fortify doesn't have a great deal of overhead. If posing a great problem, this overhead can be reduced by cutting down on the size of the fortifications, and turning off the pre/post fills, but each thing you turn off gives Fortify less information to work with in tracking your bugs. (And besides, the slower your program runs while you're developing it, the more efficient your algorithms are likely to be!). Dynamic Link Libraries ---------------------- Dynamic Linking of the standard libraries will not work with Fortify. The standard library DLL has it's own copy of memory management functions, so any memory allocated by the DLL will be unknown to Fortify, and you will get erroneous error messages. If you are writing your own DLL's, or using a 3rd party one, it may be possible to write a callback module to get the DLL's to call the Fortify routines in the parent application. If anyone does write a module to do this, please send it to me so I can include it in the next version. If the DLL doesn't allocate any memory, or your application doesn't call free/delete/realloc on any memory allocated by the DLL, then Fortify should work fine. Another alternative to the DLL problem is to use normal link libraries while you are developing with Fortify, and then rebuild them as DLL's for release. This of course may not be appropriate in all situations. REVISION HISTORY ---------------- Changes from V2.1 to V2.2 Added FORTIFY_WARN_ON_ZERO_MALLOC, and FORTIFY_FAIL_ON_ZERO_MALLOC, as some compilers do not allow a malloc(0) to succeed. Fixed a couple of incorrect #ifdef's. Improved some of the documentation. Fortify_LabelPointer() misbehaved when Fortify had been run-time disabled. Changes from V2.0 to V2.1 Fixed Fortify_LabelPointer(), which was broken. Changes from V2.0B to V2.0 Moved the checksum to the start of the Header, to avoid alignment problems (thanks to Ron Flory and Tim Taylor for finding this) Fixed a miscalcultation with FORTIFY_ALIGNED_BEFORE_SIZE (thanks to Tim Taylor) Implemented the delete stack - Fortify is now aware of deletes inside deletes, and outputs the full delete stack with all delete related hits. Implemented the new and improved Fortify_Disable() - works even if memory has already been allocated. Fixed a few problems with FORTIFY_TRACK_DEALLOCATED_MEMORY being turned off. Fixed a couple of incorrect sprintfs. Added Fortify_LabelPointer (thanks to Steve Toal for this idea) FORTIFY_WARN_ON_ZERO_ALLOCATE removed as "new char[0]" is allowed, as is malloc(0). free(0) is now accepted as harmless. Changes from V1.0 to V2.0 B Added FORTIFY_ALIGNMENT, so that machines requiring special alignment wouldn't break. Added deallocated memory tracking, and loads of diagnostics made possible by this. Added statistics. Merged Fortify and ZFortify. Each memory block now knows which allocator function was used to allocate it, and uses this information to ensure the correct function is being used to free it. Added support for strdup() Added support for versions of sprintf that don't support "%p". Fortify will now supply default values for configuration parameters missing from "ufortify.h" A few minor portability bug fixes Fortify will now automatically configure itself for GCC and Borland C++ 4.5. All error messages and warnings tidied up so they are easier to understand. The new operators will now call the new handler if the allocation fails (if there is a new handler installed, of course). Changes from SafeMem to Fortify V1.0 Completely rewrote it. Fortifies ANSI memory allocation functions rather than Amiga OS ones. Supports C++'s new and delete TO DO ----- Make Fortify automatically configure itself for major compilers. Add support for non-standard new-handlers, such as Microsoft Visual C++ 2.0, via FORTIFY_NEW_HANDLER_FUNC. Implement a stack for scope source-code info and scoped statistics. Better documentation. FAQ - installation problems, linking problems, "spurious output", understanding the results, how to track down hits. Better test programs (with examples of obscure bugs). Memory allocation list could be implemented as a "skip list", so that searching would be quicker. "Paranoid" mode would be quite fast. CHECK_ALL_MEMORY_ON_ could take a percentage of the time to do it - it could be set to 50%, so that memory is only checked 1/2 of the time, or we only check 1/2 of the memory, or something like that, so that the checking still goes on, but less frequently, so there is less overhead. Probably overkill. Write a DLL callback module so that user DLL's can share memory with their parent applicatoins. Add a "pass-count" to source-code information.