In C, automatic type conversion works on the principle of "usual arithmetic conversions". When participating in an expression with numbers of different signs (signed/unsigned), the following rules apply:
Example of dangerous arithmetic:
int a = -1; // signed unsigned int b = 1; printf("%d\n", a < b); // always false, as a is converted to a very large unsigned
The result: -1, when converted to unsigned, becomes a very large positive number.
What to remember:
Question: What result will the expression (int)(unsigned)-1 return?
Expected incorrect answer: "-1, since -1 is cast to int."
Correct answer:
In the expression (unsigned)-1, -1 is first converted to unsigned (on a 32-bit platform this is 0xFFFFFFFF), then back to signed int, which also depends on implementation, but often results in -1 again (if two's complement is used). However, it's more accurate to say: The result depends on the standards for representing signed numbers, but in most implementations it will be -1.
Example:
int x = (int)(unsigned)-1; // x == -1 on most platforms
Story
In a string handler, a size comparison function was used: if the string length could be negative, the program reported an error. However, the length was of type size_t (unsigned), and the comparison code
if(length < 0)always returned false, leading to an infinite loop and memory overflow.
Story
When parsing protocols, network packets contained fields as unsigned, while local variables were signed. Due to unsigned overflow when processing certain values, incorrect length calculations for the packet arose, resulting in a buffer overflow vulnerability.
Story
A date comparison module in logs stored dates as unsigned int but searched for date range in int. Some boundary values, instead of the expected exception, led to improper record filtering and loss of important logs.