410 lines
18 KiB
Plaintext
410 lines
18 KiB
Plaintext
|
|
Fortify - fortified memory allocation shell for C and C++
|
|
---------------------------------------------------------
|
|
written by Simon P. Bullen
|
|
Cybergraphic
|
|
|
|
Release 1.0, 2/2/1995
|
|
|
|
|
|
|
|
|
|
This software is not public domain. All material in
|
|
this archive is © Copyright 1995 Simon P. Bullen. The
|
|
software is freely distrubtable, with the condition that no
|
|
more than a nominal fee is charged for media. Everything in
|
|
this distrubution must be kept together, in original,
|
|
unmodified form.
|
|
The files 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 responsibilty for damage caused
|
|
by this software.
|
|
|
|
|
|
|
|
email: sbullen@ozemail.com.au
|
|
snail: Simon P. Bullen
|
|
PO BOX 12138
|
|
A'Beckett St
|
|
Melbourne 3000
|
|
Australia
|
|
|
|
|
|
|
|
CONTENTS
|
|
--------
|
|
Your archive should have the following files:
|
|
|
|
file_id.diz
|
|
fortify.c
|
|
fortify.doc
|
|
fortify.h
|
|
read.me
|
|
test.c
|
|
ufortify.h
|
|
ufortify.hpp
|
|
zfortify.cpp
|
|
zfortify.hpp
|
|
ztest.cpp
|
|
|
|
|
|
|
|
|
|
OVERVIEW
|
|
--------
|
|
Fortify is a descendant of a library I wrote way back in 1990 called
|
|
SafeMem. It is a (fortified) shell for memory allocations. It works with
|
|
the malloc/free family of functions, as well as new/delete in C++. It can
|
|
be adapted to most memory management functions; the original version of
|
|
SafeMem worked only with the Amiga's AllocMem/FreeMem. I haven't been
|
|
writing much Amiga specific software lately, so the Amiga version of
|
|
Fortify is way out of date, hence it's absense from this archive.
|
|
|
|
Fortify is designed to detect bad things happening, or at the very
|
|
least encourage intermittent problems to occur all the time. It is capable
|
|
of detecting memory leaks, writes beyond and before memory blocks, and
|
|
breaks software that relies on the state of uninitialized memory, and
|
|
software that uses memory after it's been freed.
|
|
|
|
It 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 sentinals (which are used to
|
|
detect writing outside the bound of the user's memory).
|
|
|
|
|
|
|
|
|
|
Fortify VERSUS ZFortify
|
|
-----------------------
|
|
Fortify provides fortification for malloc/realloc/free, and ZFortify
|
|
provides fortifications for new/delete. It is possible to use both
|
|
versions in the one program, if you are mixing C and C++. Just make sure
|
|
(as you already should be) that you never free() memory you new'd, and
|
|
other such illegal mismatching.
|
|
(Why _Z_ fortify? It's a long story...)
|
|
|
|
|
|
|
|
Fortify INSTALLATION (C)
|
|
------------------------
|
|
To use Fortify, each source file will need to #include "fortify.h". To
|
|
enable Fortify, define the symbol FORTIFY. If FORTIFY is not defined, it
|
|
will compile away to nothing. If you do not have stdout available, you may
|
|
wish to set an alternate output function. See Fortify_SetOutputFunc(),
|
|
below.
|
|
You will also need to link in fortify.o
|
|
|
|
|
|
|
|
|
|
ZFortify INSTALLATION (C++)
|
|
---------------------------
|
|
The minimum you need to do for ZFortify is to define the symbol
|
|
ZFORTIFY, and link zfortify.o. Each source file should also include
|
|
"zfortify.hpp", but this isn't strictly necessary. If a file doesn't
|
|
#include "Fortify.hpp", then it's allocations will still be fortified,
|
|
however you will not have any source-code details in any of the output
|
|
messages (this will be the case for all libraries, etc, unless you have the
|
|
source for the library and can recompile it with Fortify).
|
|
If you do not have stdout available, you may wish to set an alternate
|
|
output function, or turn on the AUTOMATIC_LOGFILE. See
|
|
ZFortify_SetOutputFunc() and AUTOMATIC_LOGFILE, below.
|
|
|
|
|
|
|
|
|
|
COMPILE TIME CUSTOMIZATIONS
|
|
---------------------------
|
|
The files "ufortify.h" and "ufortify.hpp" contain a number of #defines
|
|
that you can use to customize Fortify's behavior.
|
|
|
|
|
|
#define ZFORTIFY_PROVIDE_ARRAY_NEW
|
|
|
|
Some C++ compilers have a separate operator for newing arrays. If your
|
|
compiler does, you will need to define this symbol. If you are unsure,
|
|
dont worry about it too much, your program won't compile or link without
|
|
the correct setting. GCC 2.6.3 and Borland C++ 4.5 both need this symbol.
|
|
Microsoft C++ 1.5 and SAS 6.5 C++ both dont.
|
|
|
|
|
|
#define FORTIFY_STORAGE
|
|
|
|
#define ZFORTIFY_STORAGE
|
|
|
|
You can use this to apply a storage type to all of Fortify's exportable
|
|
functions. If you are putting Fortify in an export library for example,
|
|
you may need to put __export here, or some such rubbish.
|
|
|
|
|
|
#define FORTIFY_BEFORE_SIZE 32
|
|
#define FORTIFY_BEFORE_VALUE 0xA3
|
|
|
|
#define FORTIFY_AFTER_SIZE 32
|
|
#define FORTIFY_AFTER_VALUE 0xA5
|
|
|
|
#define ZFORTIFY_BEFORE_SIZE 32
|
|
#define ZFORTIFY_BEFORE_VALUE 0xA3
|
|
|
|
#define ZFORTIFY_AFTER_SIZE 32
|
|
#define ZFORTIFY_AFTER_VALUE 0xA5
|
|
|
|
These values define how much "fortification" is placed around each
|
|
memory block you allocate. Fortify will place _BEFORE_SIZE bytes worth of
|
|
memory right before your allocation block, and _AFTER_SIZE bytes worth
|
|
after your allocation block, and these will be initialized to _BEFORE_VALUE
|
|
and _AFTER_VALUE respectively. If your program then accidentally writes
|
|
too far beyond the end of the block, for example, Fortify will be able to
|
|
detect this (so long as you didn't happen to write in _AFTER_VALUE!).
|
|
|
|
If you don't want these fortifications to be allocated, specify a
|
|
_SIZE of 0. Note that the _VALUE parameters are 8 bits.
|
|
|
|
|
|
|
|
#define FILL_ON_MALLOC
|
|
#define FILL_ON_MALLOC_VALUE 0xA7
|
|
|
|
#define FILL_ON_NEW
|
|
#define FILL_ON_NEW_VALUE 0xA7
|
|
|
|
Programs often rely on uninitialized memory being certain values
|
|
(usually 0). If you define FILL_ON_NEW, all memory that you new will be
|
|
initialized to FILL_ON_NEW_VALUE, which you should define to be some horrid
|
|
value (definately NOT 0). This will encourage all code that relies on
|
|
uninitialized memory to behave rather differently when Fortify is running.
|
|
|
|
|
|
|
|
#define FILL_ON_FREE
|
|
#define FILL_ON_FREE_VALUE 0xA9
|
|
|
|
#define FILL_ON_DELETE
|
|
#define FILL_ON_DELETE_VALUE 0xA9
|
|
|
|
Programmers often try to use memory after they've freed it, which can
|
|
sometimes work (so long as noboby else has modified the memory before you
|
|
look at it), but is incredibly dangerous and definately bad practice. If
|
|
FILL_ON_DELETE is defined, all memory you free will be stomped out with
|
|
FILL_ON_DELETE_VALUE, which ensures that any attempt to read freed memory
|
|
will give incorrect results.
|
|
|
|
|
|
|
|
#define CHECK_ALL_MEMORY_ON_MALLOC
|
|
#define CHECK_ALL_MEMORY_ON_FREE
|
|
|
|
#define CHECK_ALL_MEMORY_ON_NEW
|
|
#define CHECK_ALL_MEMORY_ON_DELETE
|
|
|
|
CHECK_ALL_MEMORY_ON... means that for every single memory allocation
|
|
or deallocation, every single block of memory will be checked. This can
|
|
considerably slow down programs 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.
|
|
|
|
|
|
|
|
#define PARANOID_FREE
|
|
|
|
#define PARANOID_DELETE
|
|
|
|
PARANOID_... - This means that zFortify traverses the memory list to
|
|
ensure the memory you are about to free was really allocated by it. You
|
|
probably only need this in extreme circumstances. Not having this defined
|
|
will still trap attempts to free memory that wasn't allocated, unless
|
|
someone is deliberately trying to fool zFortify.
|
|
Paranoid mode adds considerable overhead to freeing memory, especially
|
|
if you are freeing things in the same order you allocated them (Paranoid
|
|
mode is most efficient if you are freeing things in the reverse order they
|
|
were allocated).
|
|
|
|
|
|
|
|
#define WARN_ON_MALLOC_FAIL
|
|
#define WARN_ON_ZERO_MALLOC
|
|
#define WARN_ON_FALSE_FAIL
|
|
#define WARN_ON_UNSIGNED_LONG_OVERFLOW
|
|
|
|
#define WARN_ON_NEW_FAIL
|
|
#define WARN_ON_ZERO_NEW
|
|
|
|
These defines enable the output of warning that aren't strictly errors,
|
|
but can be useful to determine what lead to a program crashing.
|
|
WARN_ON_NEW_FAIL causes a debug to be issued whenever new fails.
|
|
WARN_ON_ZERO_NEW causes a debug to be issued whenever a new of a zero
|
|
byte object is attempted. This is fairly unlikely in C++, and is much more
|
|
likely when using malloc().
|
|
WARN_ON_FALSE_FAIL causes a debug to be issued when a new is "false"
|
|
failed. ZSee Fortify_SetNewFailRate() for more information.
|
|
WARN_ON_UNSIGNED_LONG_OVERFLOW causes Fortify to check for breaking the
|
|
32 bit limit. This was more of a problem in 16-bit applications where
|
|
breaking the 16 bit limit was much more 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 AUTOMATIC_LOG_FILE
|
|
#define LOG_FILENAME "fortify.log"
|
|
#define FIRST_ERROR_FUNCTION
|
|
|
|
If AUTOMATIC_LOG_FILE is defined (C++ version /ZFortify only), then
|
|
Fortify will be automatically started for you, Fortify messages sent to the
|
|
named log file, and a list of unfreed memory dumped on termination (where
|
|
the log file will be automatically closed for you. If no Fortify was
|
|
output, the log file will not be altered. There are timestamps in the log
|
|
file to ensure you're reading the correct messages.
|
|
FIRST_ERROR_FUNCTION will be called upon generation of the first
|
|
Fortify message, so that the user can tell 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!
|
|
|
|
|
|
|
|
#define FORTIFY_LOCK()
|
|
#define FORTIFY_UNLOCK()
|
|
|
|
#define ZFORTIFY_LOCK()
|
|
#define ZFORTIFY_UNLOCK()
|
|
|
|
In a multi-threaded environment, we need to arbitrate access to the
|
|
foritfy memory list. This is what ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK()
|
|
are used for. The calls to ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() must
|
|
nest. If no two threads/tasks/processes will be using the same Fortify at
|
|
the same time, then ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() can safely be
|
|
#defined away to nothing.
|
|
|
|
|
|
|
|
|
|
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, and do
|
|
nothing if Fortify has been disabled with Fortify_Disable(). These
|
|
functions all apply to ZFortify as well (The ZFortify versions have a
|
|
ZFortify_ prefix, of course).
|
|
|
|
Fortify_Disable() - This function provides a mechanism to disable
|
|
Fortify without recompiling all the sourcecode. It can only be called,
|
|
though, when there is no memory on the Fortify list. (Ideally, at the
|
|
start of the program before any memory has been allocated). If you call
|
|
this function when there IS memory on the Fortify list, it will issue an
|
|
error, and Fortify will not be disabled.
|
|
|
|
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 function returns the old pointer.
|
|
The default output func is a printf() to stdout. Unless you are using
|
|
AUTOMATIC_LOG_FILE, where the default is to output to the log file.
|
|
|
|
Fortify_SetNewFailRate(int Percent) - Fortify will make a new 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.
|
|
|
|
|
|
|
|
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 "char *file" and "unsigned long line"
|
|
paramters to each of the calls. (The ZFortify versions have a
|
|
ZFortify_ prefix, of course).
|
|
|
|
Fortify_CheckPointer(void *uptr) - Returns true if the uptr points to a
|
|
valid piece of Fortify'd memory. The memory must be on Fortify's list, and
|
|
it's sentinals must be in tact. If anything is wrong, an error message is
|
|
issued (Note - if Fortify is disabled, this function always returns true).
|
|
|
|
Fortify_CheckAllMemory() - Checks the sentinals of all malloc'd memory.
|
|
Returns the number of blocks that failed. (If Fortify is disabled, this
|
|
function always returns 0).
|
|
|
|
Fortify_OutputAllMemory() - Outputs the entire list of currently
|
|
allocated memory. For each block is output it's Address, Size, the
|
|
SourceFile and Line that allocated it, and the fortify scope from within it
|
|
was allocated. If there is no memory on the list, this function outputs
|
|
nothing. It returns the number of blocks on the list, unless Fortify has
|
|
been disabled, in which case it always returns 0.
|
|
|
|
Fortify_DumpAllMemory(scope) - Just like Fortify_OutputAllMemory,
|
|
except all memory inside the given scope is output, and a hex dump of each
|
|
block is included in the output.
|
|
|
|
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 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.
|
|
|
|
|
|
PROBLEMS WITH THE new AND delete MACROS
|
|
---------------------------------------
|
|
Due to limitations of the preprocessor, getting caller sourcecode
|
|
information 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, you will need to place #undef's and #define's around
|
|
the offending code (sorry). Alternatively, you can not #include
|
|
"zfortify.hpp" for the offending file. But remember that none of the
|
|
allocation done in that file will have sourcecode information.
|
|
|
|
eg.
|
|
#undef new
|
|
void *X::operator new(size_t) { return malloc(size_t); }
|
|
#define new Fortify_New
|
|
|
|
|
|
Due to a limitation with delete, Fortify has limited information about
|
|
where delete is being called called from, and so the the line and source
|
|
information will often say "delete.0". If a delete is occuring from within
|
|
another delete, Fortify will always endeavour to report the highest level
|
|
delete as the caller.
|
|
|
|
It should be possible to replace the "new.0" and "delete.0" with the
|
|
return address of the function, which would be useful when in a debugger,
|
|
but this would be highly architecture dependant, so I leave that as an
|
|
exercise for the students :-).
|
|
|
|
|
|
|
|
WHEN TO USE FORTIFY
|
|
-------------------
|
|
The simple answer to this is "All The Time". You should never be
|
|
without Fortify when you're actually developing software. It will detect
|
|
your bugs _as_you_write_them_, which makes them a lot easier to find. One
|
|
programmer who recently started using Fortify when he had a very strange
|
|
memory problem, spent at least 3 or 4 days tracking down _other_ memory
|
|
corruption bugs that he wasn't even aware of before the program would stay
|
|
up long enough to get to his original problem. If he'd been using Fortify
|
|
from the beginning, this wouldn't have been a problem.
|
|
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_FREE, and PARANOID_FREE. With the exception of those
|
|
options, Fortify doesn't have a great deal of overhead. If posing a great
|
|
problem, this overhead can be greatly 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.
|
|
|