Question

Apple Secure Coding Guide says the following (page 27):

Also, any bits that overflow past the length of an integer variable (whether signed or unsigned) are dropped.

However, regards to signed integer overflow C standard (89) says:

An example of undefined behavior is the behavior on integer overflow.

and

If an exception occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not representable), the behavior is undefined.

Is the Coding Guide wrong? Is there something here that I don't get? I am not convinced myself that Apple Secure Coding Guide could get this wrong.

Was it helpful?

Solution 2

Let's take this example:

1 << 32

If we assume 32-bit int, C clearly says it is undefined behavior. Period.

But any implementation can define this undefined behavior.

gcc for example says (while not very explicit in defining the behavior):

GCC does not use the latitude given in C99 only to treat certain aspects of signed '<<' as undefined, but this is subject to change.

http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html

I don't know for clang but I suspect that as for gcc, the evaluation of an expression like 1 << 32 would give no surprise (that is, evaluate to 0).

But even if it is defined on implementations running in Apple operating systems, a portable program should not make use of expressions that invoke undefined behavior in the C language.

EDIT: I thought the Apple sentence was dealing only with bitwise << operator. It looks like it's more general and in that case for C language, they are utterly wrong.

OTHER TIPS

Here is a second opinion, from a static analyzer described as detecting undefined behavior:

int x;

int main(){
  x = 0x7fffffff + 1;
}

The analyzer is run so:

$ frama-c -val -machdep x86_32 t.c

And it produces:

[kernel] preprocessing with "gcc -C -E -I.  t.c"
[value] Analyzing a complete application starting at main
...
t.c:4:[kernel] warning: signed overflow. assert 0x7fffffff+1 ≤ 2147483647;
...
[value] Values at end of function main:
  NON TERMINATING FUNCTION

This means that the program t.c contains undefined behavior, and that no execution of it ever terminates without causing undefined behavior.

The two statements are not mutually incompatible.

  • The standard does not define what behaviour each implementation is required to provide (so different implementations can do different things and still be standard conformant).
  • Apple is allowed to define the behaviour of its implementation.

You as a programmer would be well advised to treat the behaviour as undefined since your code may need to be moved to other platforms where the behaviour is different, and perhaps because Apple could, in theory, change its mind in the future and still conform to the standard.

Consider the code

void test(int mode)
{
  int32_t a = 0x12345678;
  int32_t b = mode ? a*0x10000 : a*0x10000LL;
  return b;
}

If this method is invoked with a mode value of zero, the code will compute the long long value 0x0000123456780000 and store it into a. The behavior of this is fully defined by the C standard: if bit 31 of the result is clear, it will lop off all but the bottom 32 bits and store the resulting (positive) integer into a. If bit 31 were set and the result were being stored to a 32-bit int rather than a variable of type int32_t, the implementation would have some latitude, but implementations are only allowed to define int32_t if they would perform such narrowing conversions according to the rules of two's-complement math.

If this method were invoked with a non-zero mode value, then the numerical computation would yield a result outside the range of the temporary expression value, and as such would cause Undefined Behavior. While the rules dictate what should happen if a calculation performed on a longer type is stored into a shorter one, they do not indicate what should happen if calculations don't fit in the type with which they are performed. A rather nasty gap in the standard (which should IMHO be plugged) occurs with:

uint16_t multiply(uint16_t x, uint16_t y)
{
  return x*y;
}

For all combinations of x and y values where the Standard says anything about what this function should do, the Standard requires that it compute and return the product mod 65536. If the Standard were to mandate that for all combinations of x and y values 0-65535 this method must return the arithmetical value of (x*y) mod 65536, it would be mandating behavior with which 99.99% of standards-compliant compilers would already be in conformance. Unfortunately, on machines where int is 32 bits, the Standard presently imposes no requirements with regard to this function's behavior in cases where the arithmetical product would be larger than 2147483647. Even though any portion of the intermediate result beyond the bottom 16 bits will ignored, the code will try to evaluate the result using a 32-bit signed integer type; the Standard imposes no requirements on what should happen if a compiler recognizes that the product will overflow that type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top