20

In a C project (OpenVPN is the project in question, commit 4029971240b6274b9b30e76ff74c7f689d7d9750) we had a emulation of bool

typedef int bool;
#define false 0
#define true 1

and now switch to C99 bool

#include <stdbool.h>

But in the project there is somewhere a bad usage of the bool. I know that the std bool behaves different. E.g.

bool t;

t=2;
if ( t == true)
    printf("True!\n");
else
    printf("False!\n");

Will return True! with stdbool.h and False! with the #define emulation.

My question Is there a way to find these code parts that behave different with stdbool and the emulated bool? Perhaps some compiler flag I overlooked or a good llvm or gcc intermediate format that can be diffed?

It is nothing as simple as in the example above but must be something that not as easy to see. Definitively not a == true.

UPDATE: We found the issue (mbuf_set has an int member len). It kind of stupid but the question still remains how to catch these. I am surprised the integer overflow checks don't catch things like this:

 static inline bool
 mbuf_len (const struct mbuf_set *ms)
 {
   return ms->len;
 }
plaisthos
  • 6,255
  • 6
  • 35
  • 63
  • 5
    Just search for the bool literals; code that is comparing against `true` or `false` literally will have a problem. Code using the (better, imo) way of just saying `if( t )` will be fine. You could perhaps search for `true|false` and then filter that by hits for `==`. – unwind Mar 11 '13 at 12:58
  • 3
    One possibility is to use a enum instead of defines. Then, let a static code analyser parse your code and check for errors such as "t=2". But that would yield any code, also things like "t=1". – Argeman Mar 11 '13 at 12:59
  • try to write function `getTrue` and `getFalse` which will return the number of calls. after you could search like numberGetTrue == quantity `true` or not. – Dmitry Zagorulkin Mar 11 '13 at 13:23
  • If you can compile with a C++ compiler, try to create a class check_bool that has an implicit cast to bool. Override == operator, = operator (for int make it private, for check_bool make it public). Then `#define bool check_bool`. Also define true and false to objects of this class. – Aneri Mar 11 '13 at 13:35
  • I know it's not C++ but C is sometimes compilable by a C++ compiler. This is a test compilation to see where int is assigned/compared to values of bool. – Aneri Mar 11 '13 at 13:45
  • It does not compile with a C++ compiler – plaisthos Mar 11 '13 at 13:52
  • Delete the "#define" of "true" & "false", comment out the "stdbool header". Compile the code, and you will get the error of "Not defined variable" type for all "true" & "false". – Abhineet Mar 11 '13 at 14:03
  • @Abhineet That does not really help much I am afraid. It is none of occurences of false or true that are the problem – plaisthos Mar 11 '13 at 14:20
  • @plaisthos: How much of the code would have to be changed to make it compilable as C++? You could consider (temporarily) adding `#ifdef __cplusplus__ ... #else ... #endif` where necessary. – Keith Thompson Mar 11 '13 at 15:09
  • @Argeman: The static code analyzer would have to impose stricter requirements than the C standard. C enumeration constants are of type `int`, so `t = 1` is exactly equivalent to `t = true`. – Keith Thompson Mar 11 '13 at 15:10
  • @KeithThompson That is exactly what PCLint (for example) does – Argeman Mar 11 '13 at 15:59

1 Answers1

2

The kind of usage you're describing is correct, well defined behaviour. So the compiler will not produce any warnings. One possible way around this is to change the typedef:

typedef enum {false, true} bool;

This will still allow the code to compile without error (as it's well defined), but you may be able to force warnings from a compiler or analyser. For example, clang will pick this kind of thing up with -Weverything:

$ clang -o a a.c -Weverything
a.c:7:11: warning: integer constant not in range of enumerated type 'bool'
      [-Wassign-enum]
        bool n = 2;

Of course this will not do any runtime checking. It will still allow the typedef bool variable to be changed to something other than 0 or 1 (e.g. via a function call or in an expression). The only way to detect those instances is to use a debugger.

The macros for true and false in stdbool.h are designed really only for the _Bool type. This is because this type can only hold the values 0 and 1; any value you assign that isn't 0 is stored as 1. So for a boolean type only, the true and false macros are guaranteed to work.

Without the _Bool type, there's no way to have the language itself do this for you directly, because there's no comparable type, and you'd effectively be asking it to allow that 2 == 1 returns true.

There are a few ways to implement the same behaviour, e.g. using a macro such as BOOL(n) in every instance of using the variable n, to ensure its value is 0 or 1 only. This way, you'd get the same result whether using _Bool or an int for n. For example:

#define BOOL(n) ((n) != 0 ? 1 : 0 )

bool b = rand() % 100;

if (BOOL(b) == true) ...

This would work whether using stdbool or a typedef.

teppic
  • 8,039
  • 2
  • 24
  • 37
  • I know that the logic changed. I don't to emulate new bools. I want to find out why the code does not work with stdbool – plaisthos Mar 11 '13 at 13:54
  • @plaisthos Because without `stdbool`, `if ( t == true)` is the same as `if ( 2 == 1)`. – teppic Mar 11 '13 at 13:56
  • Please reread my question. My example is only to demostrate that logic is different – plaisthos Mar 11 '13 at 13:57
  • @plaisthos - the same is true for any case where `t` doesn't equal 1. The logic isn't different, it's just that `_Bool` cannot be anything other than 0 or 1. – teppic Mar 11 '13 at 13:59
  • I don't want to implement the same behaviour. I want to find the code parts that behave different because of the different behaviour. Adding a #define BOOL does not help me because I would have to change every if involving a bool variable. – plaisthos Mar 11 '13 at 14:02
  • call like you want. There other compiler switches that warn you about things are no errors. Like warning about if( a=5 ) instead of if ( a==5). Bottom line I want to the code that makes the program behave different under stdbool and #define bool. – plaisthos Mar 11 '13 at 14:19
  • @plaisthos - I've added to the answer to give an idea of how to get that info – teppic Mar 11 '13 at 14:32
  • @plaisthos If teppic is correct `typedef enum {false, true} bool` in place of the #define will produce a compiler warning precisely on the lines you are concerned about. How is this not what you are asking for? – djechlin Mar 11 '13 at 14:42
  • @djechlin the first version of the post did not have the enum idea. But unfortunatly it does not catch the error. – plaisthos Mar 11 '13 at 14:46
  • @plaisthos can you explain how it does not catch the error with `-Weverything` as a warning? – djechlin Mar 11 '13 at 14:47
  • @dijechlin If I do the define trick openvpn still compiles without warning which means that the problem I am seeing cannot be caught by the enum trick. – plaisthos Mar 11 '13 at 14:50
  • @plaisthos: this will not enforce anything, so it won't catch `bool b = foo()` where `foo` returns an arbitrary value like 2. That's impossible in C. – teppic Mar 11 '13 at 14:51