746 lines
34 KiB
Text
746 lines
34 KiB
Text
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 "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>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.
|
||
|