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

Reply via email to