Proper Use of calloc()

B

brian

I went through my implementation's (BSD) source code for
calloc(), and if no more than one object is being allocated,
the code will still execute the following if() (from
/usr/src/lib/libc/stdlib/calloc.c):

if (num && size && SIZE_T_MAX / num < size) {
errno = ENOMEM;
return NULL;
}

Is it better practice to just malloc(sizeof(type))
and then memset() the allocation, rather than calling
calloc() if only one object is being allocated?

What advantage is gained by the above if(), if malloc()
did not return NULL?

Brian
 
G

Giorgos Keramidas

brian said:
I went through my implementation's (BSD) source code for
calloc(), and if no more than one object is being allocated,
the code will still execute the following if() (from
/usr/src/lib/libc/stdlib/calloc.c):

if (num && size && SIZE_T_MAX / num < size) {
errno = ENOMEM;
return NULL;
}

Is it better practice to just malloc(sizeof(type)) and then memset()
the allocation, rather than calling calloc() if only one object is
being allocated?

What advantage is gained by the above if(), if malloc() did not return
NULL?

In my FreeBSD source tree this part of calloc() is *before* malloc() has
been called at all.

Obviously, the purpose of the particular if check is to avoid attempting
to malloc() a huge amount of space, that cannot be satisfied anyway.

This way malloc() doesn't even have to run, which probably saves a lot
of the work it would do attempting to allocate this large amount of
memory -- only to discover half-way through the allocation that it can't
be satisfied and revert all the work malloc() has done up to that point
just to return NULL to calloc().

- Giorgos
 
C

Chris Torek

In my FreeBSD source tree this part of calloc() is *before* malloc() has
been called at all.
Indeed.

Obviously, the purpose of the particular if check is to avoid attempting
to malloc() a huge amount of space, that cannot be satisfied anyway.

Actually, it is more significant than that.

The two arguments to calloc() both have type "size_t", which is an
unsigned type. Suppose (for argument's sake, or because it also
happens to be true for the Intel-based FreeBSD anyway) that size_t
is an alias for a 32-bit unsigned integer type.

Suppose further that the parameters to calloc() are 10000000 (10
million) and 65536 -- i.e., 0x00989680 and 0x00010000. (Since
multiplication is commutative, it does not really matter which of
these is "num" and which is "size".) What is the product, num*size?

Clearly it *should* be 655360000000, or 0x9896800000, but this
exceeds 32 bits. Due to the nature of unsigned arithmetic, the
result will be reduced modulo $2^{32}$ (TeX notation), giving
0x96800000 or 2524971008.

Chances are this particular malloc() will then fail, but if it
succeeds -- which is more likely if I were to fiddle the numbers
above to come up with smaller final result -- the calloc()
routine will then bzero() the same amount of memory, and finally
return that pointer to its caller -- which will expect to have
much more memory than it actually does.

This could open a security hole, depending on the application in
question and what it does with the memory it believes it has.

The OP (identified only as "brian" above) did ask why this is done
even "if no more than one object is being allocated", i.e., if num
< 2. Since num is an unsigned type (size_t), this means either
num==0 or num==1. If num==0, the first part of the test -- "num
&&" -- fails and the "if" does nothing. So the work is only done
"questionably" if num==1 (and then if size > 0). In this case,
SIZE_T_MAX / num is SIZE_T_MAX / 1 which is clearly still SIZE_T_MAX,
and since "size" also has type size_t, the test SIZE_T_MAX < size
will fail. (The varable "size" is always less than or equal to
SIZE_T_MAX.) So in this case, the test is redundant -- but so
what?

One could argue that the code might be a hair faster for the num==1
cases if the test read:

if (num > 1 && size && SIZE_T_MAX / num < size)

which is probably true, but probably also not really very interesting
-- not unless profiling shows calls to calloc() with num==1 being
high on the time-consumption list.
 
B

brian

Chris said:
The OP (identified only as "brian" above) did ask why this is done
even "if no more than one object is being allocated", i.e., if num
< 2. Since num is an unsigned type (size_t), this means either
num==0 or num==1. If num==0, the first part of the test -- "num
&&" -- fails and the "if" does nothing. So the work is only done
"questionably" if num==1 (and then if size > 0). In this case,
SIZE_T_MAX / num is SIZE_T_MAX / 1 which is clearly still SIZE_T_MAX,
and since "size" also has type size_t, the test SIZE_T_MAX < size
will fail. (The varable "size" is always less than or equal to
SIZE_T_MAX.) So in this case, the test is redundant -- but so
what?

One could argue that the code might be a hair faster for the num==1
cases if the test read:

if (num > 1 && size && SIZE_T_MAX / num < size)

which is probably true, but probably also not really very interesting
-- not unless profiling shows calls to calloc() with num==1 being
high on the time-consumption list.

The original question was why use calloc() instead of malloc() with
memset() if only one object (i.e., num == 1) is being used. I asked
because you now have to go through the if() statement mentioned in
my op. You could avoid going through the if() by just calling malloc()
and setting the space to '\0' with memset.

Thus, this introduces the question, what is gained through going through
the if() statement? And thereby, asks, what is the proper use of
calloc()? Or when should calloc() be used instead of malloc() with
memset()?

The reason why I think that is an important question is because the if()
statement is the piece that differentiates malloc() with memset() from
calloc().

brian

p.s. The worse part about the if() statement is that it isn't easy to
read.
 
G

Giorgos Keramidas

Chris Torek said:
brian said:
I went through my implementation's (BSD) source code for
calloc(), [...]

Obviously, the purpose of the particular if check is to avoid attempting
to malloc() a huge amount of space, that cannot be satisfied anyway.

Actually, it is more significant than that.

Thanks!

That was a very enlightening read. More than the 'obvious' reason is
actually hidden in that if check and now I know about it :)
 
P

pete

brian said:
Or when should calloc() be used instead of malloc() with
memset()?

Sometimes when I allocate memory for strings,
I want all the bytes zeroed. That's when I use calloc.
 

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