Malloc/Free - freeing memory allocated by malloc

P

Peter

Is it really necessary to call free()? Can I make the assumption that
the OS will do the clean up once the program terminates?

For a linked list, I can step through the list and free one node at a
time, but what about a dynamic array created by malloc?

int *data = malloc(100 * sizeof(int));

Does free(data) frees all 100 elements or only the first one? Do I
have to do the following in a loop?

free(data[0]);
free(data[1]);
free(data[2]);
...
...

I appreciate your input.

Peter
 
J

Jens.Toerring

Peter said:
Is it really necessary to call free()? Can I make the assumption that
the OS will do the clean up once the program terminates?

On most OS's you can, but if that's a good idea is another question,
especially if the code may become part of a larger program later on
(so what's now main() becomes a function of that larger program).
For a linked list, I can step through the list and free one node at a
time, but what about a dynamic array created by malloc?
int *data = malloc(100 * sizeof(int));
Does free(data) frees all 100 elements or only the first one? Do I
have to do the following in a loop?

It frees all of them. You must call free() exactly once for each time
you called malloc() (or calloc() etc.). You are only allowed to use
the pointer as you received it from malloc() in the call of free(),
nothing else.
Regards, Jens
 
G

Gordon Burditt

Is it really necessary to call free()? Can I make the assumption that
the OS will do the clean up once the program terminates?

You cannot make the assumption that the program is supposed to
terminate. Daemons don't. (and code that is reusable may be
incorporated into a daemon.) Sloppy programming with memory leaks
is likely to result in a system that has to be rebooted frequently
just to get back leaked memory.
For a linked list, I can step through the list and free one node at a
time, but what about a dynamic array created by malloc?

One malloc, one free. Many mallocs, many frees.
int *data = malloc(100 * sizeof(int));

Does free(data) frees all 100 elements or only the first one? Do I

If you got it in one malloc, all you need is one free.
malloc() does NOT know how you are using the memory.
have to do the following in a loop?

free(data[0]);
free(data[1]);
free(data[2]);
...
...

free() takes a pointer as an argument. You are feeding it an integer
argument here.

Gordon L. Burditt
 
J

Jason Curl

On most OS's you can, but if that's a good idea is another question,
especially if the code may become part of a larger program later on
(so what's now main() becomes a function of that larger program).

I remember for the AmigaOS, it didn't reclaim lost memory. It didn't use
an MMU. So, it's not really a good idea to assume the OS will clean it
up for you.
 
S

SM Ryan

(e-mail address removed) (Peter) wrote:
# Is it really necessary to call free()? Can I make the assumption that
# the OS will do the clean up once the program terminates?

It depends on the OS. You have to check your OS documentation to find out.

I think it's a good idea in most cases. If someday you or someone else wants to take
your entire program and change it into a package loaded into another program, doing
complete memory management in the first place is a big help.

# For a linked list, I can step through the list and free one node at a
# time, but what about a dynamic array created by malloc?
#
# int *data = malloc(100 * sizeof(int));

You should have one free for each malloc and for the last pointer returned by realloc.
malloc probably is unaware whether you want a very large struct or a hundred elements;
it returns one block for each call, and it is the block that has to be freed.
 
M

Malcolm

Peter said:
Is it really necessary to call free()? Can I make the assumption that
the OS will do the clean up once the program terminates?
No decent OS will allow normal applications to hog memory, even after being
terminated.
However inability to free your memory on program exit is usually a sign that
you don't have memory management under control. In particular, how will you
find genuine memory leaks?
 
P

Peter

Thanks everyone for replying, I will definitely "free" after each
"malloc". Something strange is that after calls to "free", the linked
list is still accessible, the values are still there. Should I also
assign NULL value to each node?
 
P

pete

Peter said:
Thanks everyone for replying, I will definitely "free" after each
"malloc". Something strange is that after calls to "free", the linked
list is still accessible, the values are still there.

Any test of a freed pointer invokes undefined behavior.
It may be that the memory was simply not yet reallocated
by the time that you checked it, but by checking it,
your program was still using memory that it didn't own.
Should I also
assign NULL value to each node?

I wouldn't.

This is what I use to free a list:

void list_free(list_type *node, void (*free_data)(void *))
{
list_type *next;

while (node != NULL) {
free_data(node -> data);
next = node -> next;
free(node);
node = next;
}
}

where list_type is defined as:

#define LIST_TYPE \
struct list_node { \
struct list_node *next; \
void *data; \
}

typedef LIST_TYPE list_type;
 
J

Jens.Toerring

Peter said:
Thanks everyone for replying, I will definitely "free" after each
"malloc". Something strange is that after calls to "free", the linked
list is still accessible, the values are still there. Should I also
assign NULL value to each node?

Memory may still be accessible via the pointer and it may even still
hold what it did before you called free(). But that's by pure chance,
what the pointer was pointing to may also have become inaccessible or
the memory it's pointing to may contain some other data. You simply
can't know and using the pointer after free() invokes undefined beha-
vior, i.e. everything can happen. So be very careful never to do that.

If setting the pointer to NULL after the call of free() helps you in
not using the pointer anymore then set it to NULL - but you don't
have to zero out the memory before free()ing it, that would just be
a waste of time. Setting the pointer to NULL is not required - all
you must do is refrain from using that pointer for anything at all
(including even just looking at its value).

int *x = malloc( 100 * sizeof *x );
....
free( x );

Both the following lines would invoke undefined behavior.

printf( "%d %d\n", x[ 11 ], *( x + 42 ); /* wrong! */
printf( "%p\n", ( void * ) x ); /* wrong! */

even though on some machines you might get away with doing that. All
you're allowed to do with 'x' after calling free() on it is to assign
a new value to it, e.g. by using a further call of malloc()

x = mallod( 20 * sizeof *x );

or setting it to NULL
Regards, Jens
 
D

Dan Pop

In said:
Thanks everyone for replying, I will definitely "free" after each
"malloc". Something strange is that after calls to "free", the linked
list is still accessible, the values are still there.

If you can still access those addresses, it means that the freed memory
still belongs to your program (it is available for further allocation).
The free() function typically doesn't touch the contents of the memory it
deallocates.

However, there are implementations where, under certain conditions, the
deallocated memory no longer belongs to the program. Trying to access
the freed memory will crash your program.
Should I also assign NULL value to each node?

What for? If you're concerned about security issues (you don't want your
data to be available to whatever program might "inherit" the memory you
have freed), don't worry: the operating system is supposed to clear the
memory before allocating it to another program.

Dan
 
C

CBFalconer

Peter said:
Thanks everyone for replying, I will definitely "free" after each
"malloc". Something strange is that after calls to "free", the
linked list is still accessible, the values are still there.
Should I also assign NULL value to each node?

Don't toppost. Your answer belongs after, or intermixed with, the
material you are quoting, after snipping away anything not germane
to your reply.

Having the 'values still there' is one valid form of undefined
behavior. Another is erasing your hard disk, or setting off
nuclear bombs. Don't do that.
 
D

Dan Pop

In said:
Don't toppost. Your answer belongs after, or intermixed with, the
material you are quoting, after snipping away anything not germane
to your reply.

Nope, he did the right thing. The rule has exceptions, but you need to
engage your brain to detect them.

His text was not directly related to anything he quoted, so there
was no point in not top posting. The quoted material was only
providing background context for those joining the thread at that
particular point and was of no interest for the other participants
to the discussion. No need to go again over it in order to find the
new stuff.

The ultimate proof that he did the right thing is the fact that you
didn't have to reformat his post in order to provide a meaningful reply:
all the included text could be deleted with one editor command, because
it was irrelevant to your answer.

Dan
 
J

Jason Curl

Peter said:
Thanks everyone for replying, I will definitely "free" after each
"malloc". Something strange is that after calls to "free", the linked
list is still accessible, the values are still there. Should I also
assign NULL value to each node?


Memory may still be accessible via the pointer and it may even still
hold what it did before you called free(). But that's by pure chance,
what the pointer was pointing to may also have become inaccessible or
the memory it's pointing to may contain some other data. You simply
can't know and using the pointer after free() invokes undefined beha-
vior, i.e. everything can happen. So be very careful never to do that.

If setting the pointer to NULL after the call of free() helps you in
not using the pointer anymore then set it to NULL - but you don't
have to zero out the memory before free()ing it, that would just be
a waste of time. Setting the pointer to NULL is not required - all
you must do is refrain from using that pointer for anything at all
(including even just looking at its value).

int *x = malloc( 100 * sizeof *x );
....
free( x );

Both the following lines would invoke undefined behavior.

printf( "%d %d\n", x[ 11 ], *( x + 42 ); /* wrong! */
printf( "%p\n", ( void * ) x ); /* wrong! */

I have a question about this particular line, printing the value of 'x'.
Why is this wrong? The memory allocated by x is on the function stack,
and I didn't believe you are accessing any invalid memory region?

Sure, I don't really see a point in accessing x, unless you have a
complex datastructure and you wish to free the memory, and then set
everything that was assigned to 'x' to NULL (even that can be avoided, I
only wanted to provide an example).

For example, I can see a malloc library for debugging (e.g.
ElectricFence) complaining on the first line, but not the second.
 
J

Jens.Toerring

Jason Curl said:
Both the following lines would invoke undefined behavior.

printf( "%d %d\n", x[ 11 ], *( x + 42 ); /* wrong! */
printf( "%p\n", ( void * ) x ); /* wrong! */
I have a question about this particular line, printing the value of 'x'.
Why is this wrong? The memory allocated by x is on the function stack,
and I didn't believe you are accessing any invalid memory region?

No, 'x' is obbviously not dereferenced in the second line, so no
memory is accessed that isn't owned by the program. But even
determining the address stored in the pointer after free() has
been called on it invokes undefined behavior. In the C89 standard
(or draft) you will find in the section where malloc() and free()
etc. are described the sentence

The value of a pointer that refers to freed space is indeterminate.

And later in the appendix, listing the circumstances under which
undefined behavior results, you have

The behavior in the following circumstances is undefined:

* The value of a pointer that refers to space deallocated by a call
to the free or realloc function is referred to ($4.10.3).

That means using the value of the pointer itself (which, of course,
is unchanged by the call of free()) and not what it is (or was)
pointing to. That was also quite a surprise for me when I first
learned about it;-)

But it allows to write a conforming C compiler even on a machine
that e.g. has dedicated address registers and automatically checks
on loading an address into such a register if the address belongs
to the address space of the process and traps (e.g. kills the pro-
gram) if it doesn't (with an exception for the NULL pointer). If
on such a machine free()ed memory would be removed from the pro-
cesses address space the pointer would, after the call of free(),
be invalid in the sense that loading it into an address register
would trap, even though the content (i.e. the bits stored in the
pointer) didn't changed at all. But I haven't seen a machine yet
were that would happen, but my experience isn't very large in that
respect;-)
For example, I can see a malloc library for debugging (e.g.
ElectricFence) complaining on the first line, but not the second.

ElectricFence (and probably also most of the other memory debuggers)
don't seem to check for the problem with the second line. I guess
that's using the value of a free()ed pointer isn't an issue on most
machines and checking for that could be rather difficult.

Regards, Jens
 
F

Felipe Magno de Almeida

Dan said:
If you can still access those addresses, it means that the freed memory
still belongs to your program (it is available for further allocation).
The free() function typically doesn't touch the contents of the memory it
deallocates.

However, there are implementations where, under certain conditions, the
deallocated memory no longer belongs to the program. Trying to access
the freed memory will crash your program.




What for? If you're concerned about security issues (you don't want your
data to be available to whatever program might "inherit" the memory you
have freed), don't worry: the operating system is supposed to clear the
memory before allocating it to another program.

Dan
I think it is not necessarily true, not all operating systems clear the
memory before allocating it to another program. Probably if he is really
concerned about security issues on his data that is freed he should
clear it before freeing, and even ask the SO to not let it go to its
swap space.

--
Felipe Magno de Almeida
UIN: 2113442
email: felipe.almeida@ic unicamp br, felipe.m.almeida@gmail com
I am a C, modern C++, MFC, ODBC, Windows Services, MAPI developer
from synergy, and Computer Science student from State
University of Campinas(UNICAMP).
To know more about:
Unicamp: http://www.ic.unicamp.br
Synergy: http://www.synergy.com.br
current work: http://www.mintercept.com
 
D

Dan Pop

In said:
I think it is not necessarily true, not all operating systems clear the
memory before allocating it to another program.

Concrete examples, please.
Probably if he is really
concerned about security issues on his data that is freed he should
clear it before freeing, and even ask the SO to not let it go to its
swap space.

Before calling exit() or returning from main(), do you also clear all the
statically allocated data? What about the sensitive information that
might be still existing in the stack segment, but your program can no
longer reach? What about the copy of the data that might be still
existing on the swap partition?

There is no point in attempting to do the OS job: it can do it better,
faster and more reliably.

Dan
 
M

Michael Wojcik

[Posted only to comp.lang.c; my server doesn't carry alt.comp.lang.c.]

I think it is not necessarily true, not all operating systems clear the
memory before allocating it to another program.

While this is true (there has existed at least one operating
environment where memory was not cleared before being allocated to a
program), all operating systems which meet the Orange Book C2
security criteria, or a higher level, must have "object reuse"
protection, which includes such clearing of memory.

That means that most C programmers writing code for general-purpose
OSes will not have to worry about manually clearing memory, at least
for this purpose.

Of course, as we point out to people all the time on c.l.c, not all C
programs are written for general-purpose OSes. However, the subset
of conforming C implementations which provide malloc (ie either are
hosted, or provide it as an extension in a freestanding implementa-
tion), which have any "operating system" to speak of and support
multiple "processes" (so one can allocate memory freed by another),
and which also do not provide object reuse protection, is likely to
be quite small - and people working in it probably know who they are.
Probably if he is really
concerned about security issues on his data that is freed he should
clear it before freeing, and even ask the SO to not let it go to its
swap space.

If he's really concerned with the security of his data, he's looking
at a subject far too large and off-topic to discuss in comp.lang.c.
There's a wealth of research and commentary on this subject readily
available on the Internet; this is not the place to rehash a few bits
of it.

--
Michael Wojcik (e-mail address removed)

Company Secretary Finance Manager
1) Half-grey haired executives.
2) Must be waist-deep in their field of activities.
3) Must be having the know-how and the do-how of the latest
developments in their respective fields.
-- from "Appointments Vacant" section of Business India
 
C

Chris Torek

[posted only to comp.lang.c -- alt.comp.lang.c does not exist on
newsguy]
(e-mail address removed)-berlin.de wrote: [after]
int *x = malloc( 100 * sizeof *x );
....
free( x );

Both the following lines would invoke undefined behavior.

printf( "%d %d\n", x[ 11 ], *( x + 42 ); /* wrong! */
printf( "%p\n", ( void * ) x ); /* wrong! */

I have a question about this [second] line, printing the value of 'x'.
Why is this wrong? The memory allocated [for] x is on the function stack,
and I didn't believe you are accessing any invalid memory region?

Let me back things up a bit. Suppose instead of "int *x" or even
"int y", or "float z", I tell you only that I have four bytes in
memory that are set to 0x12, 0x34, 0x56, and 0x78 in sequence.

What is the value stored in this four-byte region? Is it 0x12345678?
Is it perhaps 0x78563412? Or could it even be something like
17378244361449504001963252426735616.0?

The answer is: it depends. Those four bytes are, as a 32-bit int,
the first value (0x12345678) on a SPARC or 680x0-based machine,
but the second (0x78563412) on an Intel x86-based machine. The
third (1.73782e34, or 7019017 * pow(2,91)) occurs if those four
bytes are meant to be interpreted as a 32-bit floating point number
on the x86.

Clearly, then, the value of some sequence of bytes depends on the
*interpretation* of that byte-sequence. The next question I would
like to ask, then, is this: How are the bytes making up a pointer
interpreted?

On many machines, they happen to be interpreted in precisely the
same way as some integer; but this is not the only possible
interpretation. Those who used the x86 in its early 80186 and
80286 incarnations should remember the "1-megabyte pointer", in
which the upper 16 bits represented the top 16 of the 20 bits of
the address, and the lower 16 bits represented the bottom 16 of
the 20 bits of the address, with those two values being summed:

real_20_bit_address = ((upper_16(ptr) << 4) + lower_16(ptr)) & 0xfffff;

(This means that any given physical address has lots of different
32-bit values that refer to it. This particular "feature" was the
source of a lot of problems and the term "pointer normalization".
It is also one reason that the C standards define "a < b" only for
pointers a and b into a single object, while "a == b" is defined
even if a and b point to different objects -- the equality operators
must normalize their pointers, while the relational operators are
allowed to compare only the offsets.)

Yet another interpretation was allowed on some varieties of the
x86, in which the upper 16 bits of the pointer were an index into
an (external) table, and the lower 16 bits were an offset to be
applied to the result of the table:

real_NN_bit_address = table[upper_16(ptr)] + lower_16(ptr);
/* more or less */

All of these interpretations -- and indeed almost any other
interpretation anyone can think of yesterday, today, or tomorrow
-- are allowed (but not required) by the C standard. The last of
the above, with the table-lookup step, happens to allow something
else the x86 did: the table need not contain just a "base address".
It can also contain a "valid" flag:

if (table[upper_16(ptr)].valid == 0)
throw_runtime_error("invalid address");
real_NN_bit_address = table[upper_16(ptr)].base + lower_16(ptr);

Now, all free() needs to do is contain, as one of its steps:

table[upper_16(ptr)].valid = 0;

and suddenly a bit pattern that *was* valid, before the call to
free(), is no longer valid. An attempt to print it, which used
to work, may now cause a runtime "invalid address" error.

The bit pattern has not changed. What changed is the external
table. The C standard allows this, and tells you -- the C programmer
-- not to attempt to use the value in x after passing that value
to free(), just in case free() cleared the table's "valid" bit.

Of course, as Jens wrote, you *are* allowed to overwrite x with a
new value, or with NULL. Any C compiler must make sure this works,
even if it has this kind of "valid/invalid table entry" action that
goes on with malloc() and free().

This is all part of a more fundamental issue, which C programmers
in particular should consider, because C has "C bytes" that can be
used to access hardware-level "representations" instead of
language-level "values". That issue is: the *representation* of
a value, and value itself, are different things. Values arise
through *interpretation* of some bit pattern (a "representation"),
and the process of interpretation can be quite complex. We see
this now, today, on conventional architectures, only in floating-point
numbers -- but in the past, we saw such things elsewhere. There
were good reasons for complicated interpretations for pointers,
and those reasons may well recur in the future.

["Methods of interpretation" are also the reason we see byte-order
issues in integers. If some entity takes a long string of bits
and breaks it up into groups of, say, 8 at a time, it is that entity
that chooses which order to put out the groups, and then to
re-assemble them for later re-interpretation as a larger group.
Any given machine may have its own method(s) for splitting and
combining to get 8-bit groups, but if you, as a C programmer, deal
in terms of (integral) *values*, and do your own splitting and
combining, *you* can control the results, rather than being at the
mercy of your machine(s).]
 
F

Felipe Magno de Almeida

Dan said:
Concrete examples, please.




Before calling exit() or returning from main(), do you also clear all the
statically allocated data? What about the sensitive information that
might be still existing in the stack segment, but your program can no
longer reach? What about the copy of the data that might be still
existing on the swap partition?

There is no point in attempting to do the OS job: it can do it better,
faster and more reliably.

Dan
I know gpg has a lot of code to try to make it more secure wherever it
runs on, trying even to not let the OS to put its data on the swap, and
not all OSes must do it, if all do, better. And I agree with Michael
Wojcik that here is not the place for this discussion.

--
Felipe Magno de Almeida
UIN: 2113442
email: felipe.almeida@ic unicamp br, felipe.m.almeida@gmail com
I am a C, modern C++, MFC, ODBC, Windows Services, MAPI developer
from synergy, and Computer Science student from State
University of Campinas(UNICAMP).
To know more about:
Unicamp: http://www.ic.unicamp.br
Synergy: http://www.synergy.com.br
current work: http://www.mintercept.com
 
D

Dan Pop

In said:
I know gpg has a lot of code to try to make it more secure wherever it
runs on, trying even to not let the OS to put its data on the swap, and

Does this prove *anything* at all?
not all OSes must do it, if all do, better. And I agree with Michael
Wojcik that here is not the place for this discussion.

Then why did you start it here in the first place?

Dan
 

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

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top