Assignment from Fake Boolean

In the following example a boolean flag is passed across an interface.

  • In the source object, flags were implemented using an enum.
  • In the destination object they were implemented as a bitfield.
  • The bug was hidden by the use of a C89-style typedeffed BOOL (an int)

The resulting code fails to assign the boolean flag correctly. The bug is a non-issue if C99 stdbool is used instead.

Source

#include <stdio.h> //printf

#if \_\_STDC\_VERSION\\_\_ >= 199901
#   include <stdbool.h>
    typedef _Bool BOOL;
#   define FALSE false

#else 
    /* C89 Fake bool */
    typedef int BOOL;
#   define FALSE (0)

#endif



/* Source data */
enum EFLAGS {
    FLAG_NONE = 0x00,
    FLAG_FOO = 0x01,
    FLAG_BAR = 0x02,
    FLAG_MAX
} srcFlags = FLAG_NONE;

struct SFLAGS
{
    int isFoo   :1;
    int isBar   :1;
} dstFlags = {, };


int main(void)
{
    BOOL tmpBool = FALSE;

    srcFlags |= FLAG_BAR; /* set BAR in src */

    tmpBool = (srcFlags & FLAG_BAR); /* pass in BOOL */
    /* fake BOOL: tmpBool has value 2
     * C99 _Bool: tmpBool has value true
     */

    dstFlags.isBar = tmpBool; /* set in dst */
    /* fake BOOL: isBar is assigned (2 & 0x01) = 0  !!!
     * C99 _Bool: isBar is assigned true
     */

    if (dstFlags.isBar)
    {
        printf("dstFlags.isBar is TRUE");
    }
    else
    {
        printf("dstFlags.isBar is FALSE");
    }

    return ;
}
 

Results

$ gcc -std=c89 -pedantic -Wall -o bitfield1 bitfield1.c && ./bitfield1
dstFlags.isBar is FALSE

$ gcc -std=c99 -pedantic -Wall -o bitfield1 bitfield1.c && ./bitfield1
dstFlags.isBar is TRUE

Lesson

  • Always sanitise fake BOOL with !!(x) or (x != 0)
  • Or avoid the problem entirely by using C99 _Bool