PSI sics-cvs-psi_pre-ansto
This commit is contained in:
409
fortify.doc
Normal file
409
fortify.doc
Normal file
@ -0,0 +1,409 @@
|
||||
|
||||
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 <20> 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.
|
||||
|
Reference in New Issue
Block a user