Questions about malloc()

J

Joseph Casey

Greetings.
I have read that the mistake of calling free(some_ptr) twice on
malloc(some_data) can cause program malfunction. Why is this?
With thanks.
Joseph Casey.
 
D

dandelion

Joseph Casey said:
Greetings.
I have read that the mistake of calling free(some_ptr) twice on
malloc(some_data) can cause program malfunction. Why is this?
With thanks.

Coz it screws the heap. The 'heap' is a collection of coherent
datastructures keeping track of free memory. If you call free twice on the
same block, the internal administration will messed up. The details (of
course) depend heavily on the actual implementation and hence the libc you
have.

For an insight on heaps, consult Knuth TAoCP.
 
R

Ravi Uday

Joseph said:
Greetings.
I have read that the mistake of calling free(some_ptr) twice on
malloc(some_data) can cause program malfunction. Why is this?
With thanks.
Joseph Casey.

Yes its sounds funny ...but its actually un-defined by standard !

Acc. to standard:
Description - Free function

The free function causes the space pointed to by ptr to be
deallocated, that is, made available for further allocation. If ptr
is a null pointer, no action occurs. Otherwise, if the argument does
not match a pointer earlier returned by the calloc , malloc , or
realloc function, or if the space has been deallocated by a call to
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
free or realloc , the behavior is undefined.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

- Ravi
 
E

Eric Sosman

Joseph said:
Greetings.
I have read that the mistake of calling free(some_ptr) twice on
malloc(some_data) can cause program malfunction. Why is this?
With thanks.

Imagine that both you and your landlord are forgetful
and unskilled at keeping good records. Since you intend
to be away on vacation on the day when your rent is due,
you give your landlord a check for the month's rent before
you leave. But it slips your mind (you're forgetful,
remember? REMEMBER?), and upon your return you give him
another check and an apology for the late payment.

A few days later, your bank sends you an overdraft
notice and socks you with a fee. Your landlord complains
about the bounced check, socks you with a bad-check fee,
and threatens eviction unless you can come up with the
rent Right Now. He won't accept your check (of course),
but fortunately you've made a big profit by selling a
stale cheese sandwich on eBay and you happen to have a
wad of cash in your pocket.

You've now paid the rent three times, paid two penalty
fees, and loused up your relationships with your landlord
and your bank. Now, if the landlord kept better records
this wouldn't have happened -- but (switching from the
analogy back to Standard C) the memory "landlord" isn't
required to be that careful. In fact, many "landlords"
do only the minimum amount of record-keeping, in pursuit
of greater efficiency. They're counting on you to remember
whether you have or haven't paid the rent, whether you have
or haven't free()d the memory. Sharpen your memory or
suffer the consequences.
 
G

Gordon Burditt

I have read that the mistake of calling free(some_ptr) twice on
malloc(some_data) can cause program malfunction. Why is this?
With thanks.
Joseph Casey.

Because the standard says it can.

Also, it would probably slow down malloc() and free() considerably
to require that they accept repeated calls, or any old garbage
pointer.

Gordon L. Burditt
 
K

Keith Thompson

Joseph Casey said:
I have read that the mistake of calling free(some_ptr) twice on
malloc(some_data) can cause program malfunction. Why is this?

The simple answer is that free()ing the same pointer twice invokes
undefined behavior because the standard says it invokes undefined
behavior. ("Undefined behavior" means that the standard imposes no
requirements on what happens; it can quietly do nothing, it can crash
your program, or it can make demons fly out your nose.)

As for *why* it's undefined behavior, basically the system is getting
revenge because you lied to it. By calling free(some_ptr), you're
telling the system, "Here's a pointer to a chunk of allocated memory.
I'm done with it. You can have it back now." By calling
free(some_ptr) again, you're telling the system the same thing -- but
now the chunk of memory is no longer allocated, and since you already
told the system that you were done with it, you have no right to do
anything with it. C tends to assume that you (the programmer) know
what you're doing, so it doesn't necessarily spend much extra effort
checking for your mistakes; it just takes your word for it.

The most dangerous thing about undefined behavior is that it can show
up as whatever you might naively expect. On the second call to
free(some_ptr), the system is *allowed* to say, "You've already freed
this pointer; I'll just pretend you didn't try to free it again". In
particular, it's allowed to do this during testing, so you never
detect the bug, and then blow up in your face when you're doing a demo
for an important customer.

One possible scenario is that, between the first and second calls to
free(some_ptr), you call malloc() in some other part of your program,
and it happens to re-use the chunk of memory you freed. On the second
call to free(some_ptr), some_ptr happens to point to a chunk of memory
that's being used somewhere else. The system assumes you know what
you're doing, and makes that chunk available for re-use. Later, yet
another part of your program grabs that chunk of memory *again* and
writes its own data to it, clobbering your carefully crafted dynamic
data structure. Note that the part of your program where the symptoms
appear can be very distant from the part where you caused the problem.
Hilarity ensues.
 
M

Method Man

Ravi Uday said:
Yes its sounds funny ...but its actually un-defined by standard !

Acc. to standard:
Description - Free function

The free function causes the space pointed to by ptr to be
deallocated, that is, made available for further allocation. If ptr
is a null pointer, no action occurs.

[snip]

Which brings up the question: Why did they not originally implement 'free'
to set the pointer to NULL after deallocating the memory? Other than some
obscure algorithms, I don't see much use to the programmer of having a
pointer that points to non-existant memory.
 
A

Al Bowers

Method said:
Yes its sounds funny ...but its actually un-defined by standard !

Acc. to standard:
Description - Free function

The free function causes the space pointed to by ptr to be
deallocated, that is, made available for further allocation. If ptr
is a null pointer, no action occurs.


[snip]

Which brings up the question: Why did they not originally implement 'free'
to set the pointer to NULL after deallocating the memory? Other than some
obscure algorithms, I don't see much use to the programmer of having a
pointer that points to non-existant memory.

How do you propose you do this?
Maybe
void free(void **p);
?
That looks invalid.
 
K

Keith Thompson

Method Man said:
Which brings up the question: Why did they not originally implement 'free'
to set the pointer to NULL after deallocating the memory? Other than some
obscure algorithms, I don't see much use to the programmer of having a
pointer that points to non-existant memory.

Because it can't. The pointer argument to free(), like all function
arguments, is passed by value; the function can't modify it. (A
free() function that takes a void** rather than a void* couldn't
handle arbitrary pointer types.)
 
M

Method Man

Keith Thompson said:
Because it can't. The pointer argument to free(), like all function
arguments, is passed by value; the function can't modify it. (A
free() function that takes a void** rather than a void* couldn't
handle arbitrary pointer types.)

Yea, I suppose not. It could return a NULL:

p = free(p);

I just think free()'ing p and setting p = 0 should be handled in one step
since the operations are closely related and it avoids programmer error.
 
E

Eric Sosman

Method said:
Yea, I suppose not. It could return a NULL:

p = free(p);

I just think free()'ing p and setting p = 0 should be handled in one step
since the operations are closely related and it avoids programmer error.

There's a wider problem: What makes you think that `p'
is the only pointer to the memory being free()d? If there
are other copies lying around, how are you going to find
them and zap them? What are you going to do with things
like `free(find_dead_area())'?

IMHO, dodges like `#define FREE(p) (void)(free(p), (p)=0)'
are unattractive because they pretend to solve a problem but
really don't. They encourage an unwarranted relaxation of
vigilance. YMMV.
 
C

Charlie Gordon

There's a wider problem: What makes you think that `p'
is the only pointer to the memory being free()d? If there
are other copies lying around, how are you going to find
them and zap them? What are you going to do with things
like `free(find_dead_area())'?

IMHO, dodges like `#define FREE(p) (void)(free(p), (p)=0)'
are unattractive because they pretend to solve a problem but
really don't. They encourage an unwarranted relaxation of
vigilance. YMMV.

So what !
There are other ways of getting infected so why bother with condoms...

Chqrlie.
 
R

Robert Harris

Charlie said:
So what !
There are other ways of getting infected so why bother with condoms...

Chqrlie.
The real point is: if you don't want to be bothered with integer
overflows, buffer overruns and system call failures, don't use 'C',
which will always be fast and dangerous.

Robert
 
C

Chris Barts

Robert said:
The real point is: if you don't want to be bothered with integer
overflows, buffer overruns and system call failures, don't use 'C',
which will always be fast and dangerous.

And another point is missed. The real point is, was, and always will be:
Engage your brain! Everything else is mere prattle.
 
R

Richard Bos

Method Man said:
Yea, I suppose not. It could return a NULL:

p = free(p);

I just think free()'ing p and setting p = 0 should be handled in one step
since the operations are closely related

No, they're not, except in the case of programmers who don't trust
themselves to keep their valid and invalid pointers straight.
and it avoids programmer error.

It avoids paying proper attention to your bookkeeping, which in turn
tricks people into believing that using a pointer is always safe,
because it will "always" be either valid or null. Such slackers are
guaranteed to sooner or later dereference a null pointer on a system
which doesn't trap on that, or to dereference a pointer which is wild
through broken pointer arithmetic rather than because it has been freed,
or to set a pointer to null because it's been freed, and then to use a
non-null copy of the original pointer, or...
You get my drift, I hope. _You_ need to pay attention. You cannot trust
your programs to such tricks, because there's always a situation where
they don't work, and in the mean time your vigilance has been put to
sleep.

Richard
 
C

Charlie Gordon

Richard Bos said:
No, they're not, except in the case of programmers who don't trust
themselves to keep their valid and invalid pointers straight.


It avoids paying proper attention to your bookkeeping, which in turn
tricks people into believing that using a pointer is always safe,
because it will "always" be either valid or null. Such slackers are
guaranteed to sooner or later dereference a null pointer on a system
which doesn't trap on that, or to dereference a pointer which is wild
through broken pointer arithmetic rather than because it has been freed,
or to set a pointer to null because it's been freed, and then to use a
non-null copy of the original pointer, or...
You get my drift, I hope. _You_ need to pay attention. You cannot trust
your programs to such tricks, because there's always a situation where
they don't work, and in the mean time your vigilance has been put to
sleep.

BS.

I call it defensive programming : it is certainly not perfect, but your
arguments for not doing it are stupid.
Using the same logic, you would require that :
- code should never be indented, this puts the programmer's attention to program
structure at rest and he will sooner or later get caught with a mis aligned
unbraced block.
- variables should not be named consistently, because the programmer will expect
certain semantics associated with certain names and misunderstand perfectly
correct programs.
- comments should not be used, as they may contain mistakes that the compiler
cannot spot.
- all integer constants should be written in octal : C doesn't support BCD
arithmetic, neither should alert programmers.
- spacing is a nuisance in C source code, it hides the beauty of concise C as in
a+++b, sum/*n;
- write all programs with OCCC in mind, to keep potential maintainers in shape.
 
G

Guillaume

And another point is missed. The real point is, was, and always will be:
Engage your brain! Everything else is mere prattle.

Ouch! Aren't you asking for a bit much? :D
 
E

Eric Sosman

Charlie said:
So what !
There are other ways of getting infected so why bother with condoms...

Because condoms don't provide 100% protection. If you
need 100% protection, the solution is to refrain from, er,
screwing around. And if you don't screw around, you don't
need condoms.

What it comes down to, I think, is a difference in
attitude about how to write programs that work. I've been
pondering how to express my ideas about The Right Way (tm)
to do things, and my misgivings about piecemeal approaches
like zeroing the argument to free(). Here's my poor best
at trying to explain my convoluted self:

Programs aren't life-like, in the sense that their
correctness[1] isn't a matter of probability[2]. An
incorrect program is incorrect even if it hasn't actually
failed yet; the error is latent, ready to cause a failure[3]
when the circumstances are right (or wrong, depending on
your point of view). In managing dynamic memory, omitting
to keep proper track of which pointers are "live" and which
have "died" is an error. You must have a means to tell
whether a pointer is or isn't current before you try to
use[4] it -- and if you have such a means, you don't need
any special value in the pointer itself.

Now, the "means" could perfectly well rest on the
assertion "All non-NULL pointers in my program are valid."
But clobbering just the one pointer handed to free() is
not enough to maintain the assertion: You need additional
effort and additional mechanisms to find and clobber all
the other copies that may be lying around. It's often
the case, for example, that the argument to free() is
the argument to free()'s caller, so zapping free()'s
argument is only the beginning of the job. A clear-the-
pointers scheme is far beyond the capabilities of the
simplistic dodges like the one I illustrated; it must be
driven from the top down rather than from the bottom up.

Bottom-up schemes aren't good enough for serious use.
They can even be harmful: by preventing failures in a
chunk of erroneous code 99% of the time, they can make
it less likely that the error will be exposed in testing.
They are the Typhoid Marys of programming: asymptomatic
and yet deadly. And that's why I don't like 'em.

Notes:

[1] I'm using "correctness" in the weak sense: A "correct"
program is one that doesn't "go down in flames" by doing
something like following stale pointers. Such a program
may nevertheless compute the value of pi as -42 or state
that the square root of 3 is 9 or otherwise contravene its
specification, but that's not the kind of "correctness"
I'm writing about. "Well-behaved" might have been a better
word than "correct," but I'm not up for a rewrite.

[2] This isn't meant to rule out probabilistic computation
methods. A program that seeks a solution probabilistically
and sometimes fails to find one but "plays nice" and reports
the failure in a controlled manner is still correct in the
sense of [1], and in some stronger senses as well.

[3] "Failure" as in "going off the rails." A program can
produce incorrect results without "failing;" this is the
flip side of [1].

[4] Note that simply examining the value of an invalid
pointer constitutes a "use," even if the pointer is not
dereferenced. Clearing free()d pointers can avoid this
particular problem, but you've got to get 'em all or it's
of no use.
 
C

Chris Torek

What it comes down to, I think, is a difference in
attitude about how to write programs that work. I've been
pondering how to express my ideas about The Right Way (tm)
to do things, and my misgivings about piecemeal approaches
like zeroing the argument to free(). Here's my poor best
at trying to explain my convoluted self:

[much snippage]
Bottom-up schemes aren't good enough for serious use.
They can even be harmful: by preventing failures in a
chunk of erroneous code 99% of the time, they can make
it less likely that the error will be exposed in testing.
They are the Typhoid Marys of programming: asymptomatic
and yet deadly. And that's why I don't like 'em.

At the same time, having low-level routines that "don't fail"
(or at least "don't make things worse") *can* sometimes be
valuable. As always, it comes down to a complex set of cost-benefit
equations. We (the editorial "we" here) would like our programs
to be correct, but when they do fail, we would also like them to
be debuggable.

For this sort of reason, I do not mind:

free(ptr), ptr = NULL;

but in many cases it does not really add much to debuggability.
It *does* help if (but only if) you can establish an invariant:
"ptr is either NULL, or a valid pointer" -- and that depends greatly
on the context in which "ptr" appears.
 
J

Joe Wright

Joseph said:
Greetings.
I have read that the mistake of calling free(some_ptr) twice on
malloc(some_data) can cause program malfunction. Why is this?
With thanks.
Joseph Casey.

Please pardon the late reply Joseph. Several valid replies follow
your original post last Sunday or Monday morning. I'm never sure
where to jump in on threads like this so I chose the top.

Someone many years ago (who?) chose to implement malloc()/free() in
a truly minimalist fashion. Minimalism has a long and credible
history in C but I think malloc()/free() is poorly designed.

It would be trivial for malloc/realloc/calloc to 'remember' what
they do in a List so that free() can do the right thing. If we call
free() with a 'wild' pointer the system would look it up in the List
and, not finding it, do nothing.

The List might also remember the size of the allocation and so
permit the much wanted 'size_t size(void *m);' which would return
the amount of memory allocated at a particular address, or zero if
we can't find m in the List.

Also, because the List remembers ALL *alloc() calls, we can support
yet another function, freeall(), which will free all allocated memory.

This is usenet and anybody can respond with anything. I know that
well. But these are not idle ramblings. I have implemented all of
this here at home and it works perfectly. My question in all this is
"Why wasn't it done this way the first time?".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Alternative to Malloc in C 0
Malloc Query 86
Correct use of malloc 9
malloc and maximum size 56
malloc 40
array-size/malloc limit and strlen() failure 26
Malloc question 9
Malloc Query 8

Members online

No members online now.

Forum statistics

Threads
474,156
Messages
2,570,878
Members
47,404
Latest member
PerryRutt

Latest Threads

Top