Martin Pool's blog

gcc makes my day

Ben pointed out the -ftrapv feature in gcc, something I have wanted for a while.

-ftrapv
This option generates traps for signed overflow on addition, subtraction, multiplication operations.

Example:

int main()
{
     int x = 1;
     while (1) {
          x *= 2;
          printf("%u\n", x);
     }
     return 0;
}

gcc -Wall -ftrapv overflow.c -o overflow -O3 && ./overflow >/dev/null

aborted

The current implementation seems to turn these operations into function calls, which may be a bit slow. It seems like it could potentially be done inline. But it's still something that might be very useful in test-builds or in security-sensitive software.

Random C idea: limited integers

I wonder if this could be added to gcc

int a __attribute__((limited));

So if a every overflows or underflows, the machine will will check, perhaps by raising a SIGTRAP.

How much would it cost in performance? At the worst, one conditional branch operation after each manipulation of such a variable, to check the overflow status bit. But probably only one or two additional instructions, if we only care about limiting to the size of the variable and not an arbitrary range. Maybe something more efficient is possible.

The goal is to prevent the significant class of bugs caused by an integer overflow without necessarily having a following array overflow.

This probably needs to be optional per-variable so as not to break existing code.

Maybe this has already been done or it isn't feasible or it's just silly?

warn_unused_result

gcc has a neat attribute that helps you check that function return codes are used.

For example, I have a function dcc_timeout_arm in distcc which uses longjmp and so returns a second time with an error if the timeout fails. So it's absolutely critical that the error return code be checked, or the code can't possibly work correctly.

In a earlier draft of the code I forgot to check it in some places. Unfortunately for me the non-error path works fine, but the error path would always fail. Of course error paths are notoriously poorly tested.

Therefore:

#ifdef __GNUC__
#  define WARN_UNUSED  __attribute__((warn_unused_result))
#else
#  define WARN_UNUSED
#end
...
int dcc_timeout_arm(int timeout, int) WARN_UNUSED;

So this gives a warning:

dcc_timeout_arm(5, DCC_PHASE_CONNECT);

and this is correct:

if ((ret = dcc_timeout_arm(5, DCC_PHASE_CONNECT)))
    goto out; */

Great stuff. In terms of Rusty's module interface continuum, we just went from "6: follow common convention (check return codes) and you'll get it right" to "2: compiler will warn if you get it wrong." Build with -Werror and you get up to "1: the compiler won't let you get it wrong."

I hope gcc gains more attributes like this in the future. It would be good to get things like in Splint to check more invariants.

I had thought it would be nice if there were a global option to warn about ignored return values. The gcc team say it's not good, and on reflection I agree:

10.11 Certain Changes We Don't Want to Make

[....]

Warning when a non-void function value is ignored.

C contains many standard functions that return a value that most programs choose to ignore. One obvious example is printf. Warning about this practice only leads the defensive programmer to clutter programs with dozens of casts to void. Such casts are required so frequently that they become visual noise. Writing those casts becomes so automatic that they no longer convey useful information about the intentions of the programmer. For functions where the return value should never be ignored, use the warn_unused_result function attribute.

UNUSED in gcc

Of course you build programs with warnings turned on? If not, write it out one hundred times...

-Wunused causes gcc to complain about unused parameters. This is often a good thing, if it makes you check twice whether you mistyped a variable name or forgot to implement something. But sometimes you really need to have a parameter which is not used, perhaps because the function needs to match a particular prototype.

gcc has a __attribute__((unused)) thing you can apply to paramters to quieten the warning. This actually means possibly unused: if you do use the parameter, gcc doesn't complain. So with unused alone, you can make errors in the opposite direction.

I use this little macro:

#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif

void dcc_mon_siginfo_handler(int UNUSED(whatsig))

This applies the unused attribute and also mangles the variable name so that you really can't use it. It also lets you just write UNUSED(x) regardless of compiler. Maybe there should be more branches for other compilers.

(Sometimes this won't do: you might have a parameter or variable which is only used when particular preprocessor variables are set. But in that case you might want to simplify the ifdefs anyhow.)

Tim points out that this helps shift up Rusty's interface simplicity continuum: the compiler won't let you get it wrong. The parameter is either used, or unused.

Of course you can also say

void a(int b) {
    (void) b;
    ...

I have a feeling there was some reason this was inferior to the unused attribute but I don't remember what it was.

integer promotion in varargs calls

Question: what does this do?

printf("%ld\n", 32);

You might think it prints 32. But I think the results are in fact undefined, and on a 64-bit platform you might get some value other than 32.

The problem is that vprintf (or some function inside it) will try to read off the varargs stack a value of type long, which is 64 bits on ia64 and (all?) other Linux 64-bit platforms. However, the value is a literal integer, and passed as such.

So why does this normally work? I think the reason is that on IA64, all integers are passed in 64-bit slots, with the first 8 parameteres in the frame input registers and the rest on the stack. However, there is no guarantee that the entire slot will be initialized if it's only carrying an int. At least in some cases, gcc generates a st4 instruction to store the value, so the top 32 bits are uninitialized. It seems that they often happen to be zero, but not always. This was causing a semi-intermittent failure of the Vstr test case.

You might think that all integer types are promoted to long, but that is not in fact the case. ISO/IEC 9899:1999 (E), the C specification says basically that types smaller than int are promoted to ints, and floats are promoted to doubles. There is no automatvic promotion to longs.

The correct way to write it, assuming you wanted to pass it as a long, is

printf("%ld\n", 32L);

The good news is that gcc can generally give compile-time warnings for this kind of problem, although it cannot trap every possible case, and it can't trap non-printf varargs functions.

pop quiz

What value is assigned to the macro by this line?

#define PEGASUS_ATOMIC_INT_NATIVE = 1

Bitshifts in C

What do you think this does?

uint32_t a, b;
[....]

b = 32; a = a >> b;

You might think this will reduce a down to zero. But in fact, the C99 specification says that shifting either left or right by more than the width of the type causes undefined behaviour. On gcc on i386, it in fact shifts by b % 32, so in this case a is unchanged.

Archives 2008: Apr Feb 2007: Jul May Feb Jan 2006: Dec Nov Oct Sep Aug Jul Jun Jan 2005: Sep Aug Jul Jun May Apr Mar Feb Jan 2004: Dec Nov Oct Sep Aug Jul Jun May Apr Mar Feb Jan 2003: Dec Nov Oct Sep Aug Jul Jun May