The correct fix is to cast the input to unsigned, I think.
https://stackoverflow.com/questions/27490762/how-can-i-conve...
Parameter passing works much like assignment. Just like you can assign an int value to an unsigned int variable, you can pass an int argument to an unsigned int parameter.
There is free conversion among char, int, long, and the floating-point types. For instance int x = 1.0 is valid.
Many of these converisons have implementation-defined behavior if the value doesn't fit into the range of the destination type. Assigning a long long to a char where the value does not fit, for instance.
In an integer-floating conversion or vice versa, if the value does not fit, the behavior is undefined. No diagnostic required. E.g. the program can die with a "floating point exception", or just putter along with a random incorrect value.
The conversion rules themselves are implementation-specific because the sizes and ranges of the type vary, as well as certain details like whether plain char is signed or not.
For instance, if int and long have the same range (e.g. both are 32 bits wide), then converting any value between them is well-defined. If they don't (e.g. 16 bit int, 32 bit long), then converting long to int can truncate.
Some things can be counted upon: signed char will convert to short without loss; short to int, int to long, long to long long. Also float to double.
Edit: Oh, I see what I missed -- I was thinking of the danger as directly passing too large a number to malloc, when actually the danger is passing a negative number, which then gets implicitly converged to a large positive number. Oops.
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
Gotten from S/O.Meaning -1, which passes the signed check fine, becomes 0xFFFFFFFF as a 32 bit unsigned integer...
Now, theoretically a well configured compiler that complains about one would also complain about the other, catching the signed value going to malloc as a warning.
However, if you have one signed type and one unsigned type, and the unsigned type has greater or equal rank, then it converts to the unsigned type, despite the fact that the unsigned type can't represent any of the negative values of the signed type, so they get wrapped!
This is the cause of so many bugs.
The cleverness of this implicit backdoor hypothetical is that it would have worked substantially the same way in most other low-level languages, including Rust. Modern C compilers literally complain in the same way as stricter languages, which had the effect of inducing a poorly considered cast.
I think the correct fix is rather to check the input (e.g. assert()). What you suggest means reinterpreting what the caller wanted, and that leads to nasty surprises down the road.
That said, the latest release of glibc would have failed on a negative expression promoted to unsigned:
> Memory allocation functions malloc, calloc, realloc, reallocarray, valloc, pvalloc, memalign, and posix_memalign fail now with total object size larger than PTRDIFF_MAX. This is to avoid potential undefined behavior with pointer subtraction within the allocated object, where results might overflow the ptrdiff_t type.
https://sourceware.org/ml/libc-alpha/2019-08/msg00029.html
The new glibc behavior doesn't help with the multiplication overflow, though, as it could have wrapped to a positive value (notwithstanding the fact that such wrapping is undefined). Which is why if I saw code like this I would've worked my way back to all the call sites to understand why the argument to allocatebufs wasn't size_t to begin with. Maybe they were C++ programmers--for decades "best practice" in C++ was to use int for the size of objects.
Arithmetic overflow leading to potential buffer overflow is still a problem even in languages like Rust. Nobody should ever add an integer cast somewhere without having done their due diligence. Which is why stricter arithmetic type checking can sometimes be a double-edged sword--too many people will add an explicit cast and move on.
Edit: proper explanation above. I got some things wrong.