Eric said:
[... about flood-filling freshly-allocated memory ...]
Initializing new allocations with 0xDEADBEEF is
traditional; there's no C library function to do the chore,
but it's not hard. I've also seen good results from using
memset() to fill each new area with 0x99.
So you see the benefit of using fixed patterns, right?
Whether 0x99 or 0x0, it does not matter. What matters is
you see the same (wrong) behavior on code execution.
Certainly the value of the fill matters. Pre-filling each
memory allocation attempts to cause an observable malfunction
from one particular class of bug: Fetching something from the
memory area without storing a "real" value first. All-bits-zero
is quite likely to be mistaken for a legitimate NULL (at the
"end" of a linked list gone astray, say), or for an ordinary
zero-valued integer or floating-point value. A program that
fetches such a thing from "uninitialized" memory stands a
reasonable chance of stumbling ahead successfully despite its
bug, producing no malfunction that a tester might notice.
A value like 0x99, on the other hand, has several chances
to produce nastier and more overt misbehavior:
- A pointer full of 0x99's is unlikely to satisfy the
alignment requirements (if any exist) of anything wider
than a `char'. Pluck an "uninitialized" pointer from a
memory area and use it to address a struct or to call a
function, and there's a good chance of something like
SIGBUS.
- A signed integer full of 0x99's is likely to be negative.
In many situations a negative number is nonsensical, and
may cause erroneous behavior a zero would not provoke.
Even if it's nothing worse than "Summary: -1717986919
errors detected" it'll raise more testers' eyebrows than
if the reported number were zero.
- A size_t full of 0x99's is likely to be "very large," so
large that it stands a chance of causing trouble. A call
like memcpy(&target, &source, 0x9999999999999999) is almost
sure to produce a trap of some kind.
- A string full of 0x99's has no terminator, and may well
cause trouble if passed to strlen() or printf() or whatever.
A string full of zeros has a valid terminator, and will
not cause trouble if you strcpy() from it.
Other fill patterns have their advantages, too: Filling
freshly-allocated memory with signalling NaNs seems a promising
avenue, for example. A good debugging allocator should probably
be able to use different fill patterns depending on an environment
variable or some such.[*]
[*] On one system I used years ago, privileged code could
set and clear the memory parity bits at will, regardless of the
data; the capability was used in diagnostic programs. One fellow
attempted to exploit this as a cheap way of catching this class
of bug: He'd set bad parity in uninitialized memory areas. If
the program wrote to the area it would regenerate correct parity
in the process, but if it read before reading there'd be a machine
malfunction trap which he could intercept. Unfortunately, if the
program crashed for some other reason and the core dump routine
came along ... We had to take the imaginative fellow aside and
speak to him rather sternly.
True, if you can clearly see that is the behavior, malloc
fits the bill. Otherwise the extra developer time saved
in debugging because of calloc usage is lot more valuable
than the saving of a little performance on its non use.
We agree on the purpose, but not on the tactics. I feel
that a "clean" zero is less likely to expose a latent bug than
is a fill pattern constructed with diabolic intent. Also, if
the developer saves debugging time at the cost of letting more
bugs through, the trade-off needs justification (I'm not saying
it's always unjustifiable, just that it's a risk that needs
assessment). Finally, note that the use of calloc() makes the
use of poisonous fill patterns impossible and makes this bug-
provoking technique unavailable.