On Thu, Feb 18, 2021 at 03:24:36PM -0600, Luke Small wrote:
> However, calloc(ptr, nmemb, size) may have been called using smaller int
> variable types which would overflow when multiplied. Where if the variables
> storing the values passed to nmemb and size are less than or especially
> equal to their original values, I think it’d be good to state that:
>
> freezero(ptr, (size_t)nmemb * (size_t)size);
> is guaranteed to work, but
> freezero(ptr, nmemb * size);
> does not have that guarantee.
Lets try to make things explicit.
The function c() does the overflowe check like calloc does.
The function f() takes a size_t.
#include <limits.h>
#include <stdio.h>
#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
void c(size_t nmemb, size_t size)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_T_MAX / nmemb < size)
printf("Overflow\n");
else
printf("%zu\n", nmemb * size);
}
void f(size_t m)
{
printf("%zu\n", m);
}
int
main()
{
int a = INT_MAX;
int b = INT_MAX;
c(a, b);
f(a * b);
}
Now the issues is that the multiplication in the last line of main()
overflows:
$ ./a.out
4611686014132420609
1
because this is an int multiplication only after that the promotion to
size_t is done.
So you are right that this can happen, *if you are using the wrong
types*. But I would argue that feeding anything other than either
size_t or constants to calloc() is already wrong. You *have* to
consider the argument conversion rules when feeding values to calloc()
(or any function). To avoid having to think about those, start with
size_t already for everything that is a size or count of a memory
object.
-Otto