How can bool variable be not equal to both True an

2019-09-18 13:56发布

According to accepted answer of this question What is the benefit of terminating if … else if constructs with an else clause?

There is a corruption case (in embedded system) that can cause a bool variable (which is 1 bit) differ to both True and False, it means the else path in this code could be covered instead of be a dead code.

if (g_str.bool_variable == True) {
 ...
}
else if (g_str.bool_variable == False) {
 ...
}
else {
 //handle error
}

I try to find out but there's still no clue for it.

Is it possible ? and How ?

Edit: For more clearly, I will give the declaration of the bool variable like:

struct {
   unsigned char bool_variable : 1;

} g_str;

And also define:

#define True 1
#define False 0

标签: c embedded
4条回答
手持菜刀,她持情操
2楼-- · 2019-09-18 14:34

The way you've defined things, this wouldn't happen on an x86. But it could happen with some compiler/cpu combination.

Consider the following hypothetical assembly code for the if-else-else construct in question.

    mv SP(0), A             # load 4 bytes from stack to register A  
    and A, 0x1              # isolate bit 1 i.e. bool_variable
    cmp A, 0x1              #  compare to 1 i.e. True
    jmp if equal L1
    cmp A, 0x0              #  compare to 0 i.e. False
    jmp if equal L2
    <second else block>
    jmp L3
L1:
    <if block>
    jmp L3
L2:
    <first else block>
L3:
    <code>

Now consider the hypothetical machine code for some of these instructions.

             opcode-register-value   machine-code   corrupted-code
and A, 0x1     01      03      01      010301          010303
cmp A, 0x1     02      03      01      020301          020302
cmp A, 0x0     02      03      00      020300          020304

One or more of bit corruptions shown above will cause the code to execute the second else block.

查看更多
小情绪 Triste *
3楼-- · 2019-09-18 14:37

unsigned char bool_variable : 1 is not a boolean variable. It is a 1 bit integer bit-field. _Bool bool_variable is a boolean variable.

A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type. It is implementation-defined whether atomic types are permitted. > C11dr §6.7.2.1

So right away unsigned char bool_variable : 1, it is implementation-defined if it is allowed.


If such an implementation treated unsigned char bit-fields like int bit-fields, as unsigned char range can fit in int range, then troubles occur with 1-bit int bit-fields. It is implementation defined if a 1-bit int bit-field takes on the values of 0, 1 or 0, -1. This leads to the //handle error clause of this if() block.

if (g_str.bool_variable == True) { // same as if (g_str.bool_variable == 1)
 ...
}
else if (g_str.bool_variable == False) { // same as if (g_str.bool_variable == 0)
 ...
}
else {
 //handle error
}

The solution is to simplify the if() test:

if (g_str.bool_variable) {
 ...
}
else  {
 ...
}

With bit-fields, it is a corner in C where unsigned int, signed int are different, but int bit-fields less than the full width of an int may be treated as signed int or unsigned int. With bit-fields, it is best to be explicit and use _Bool, signed int, unsigned int. Note: using unsigned is synonymous with unsigned int.

查看更多
一纸荒年 Trace。
4楼-- · 2019-09-18 14:45

The reason I wrote that example like it did, using "mybool", FALSE and TRUE, was to indicate that this is a non-standard/pre-standard boolean type.

Before C got language support for boolean types, you would invent your own boolean type like this:

typedef { FALSE, TRUE } BOOL;

or possibly:

#define FALSE 0
#define TRUE  1
typedef unsigned char BOOL;

In either situation you get a BOOL type which is larger than 1 bit, and can therefore either be 0, 1 or something else.

Had I written the same example using stdbool bool/_Bool, false and true it wouldn't have made any sense. Because then the compiler might implement the code as a bit-field and a single bit can only have values 1 or 0.


In retrospect, a better example of the use of defensive programming might have been something like this:

typedef enum
{
  APPLES,
  ORANGES
} fruit_t;

fruit_t fruit;

if(fruit == APPLES)
{
  // ...
}
else if(fruit == ORANGES)
{
  // ...
}
else
{
  // error
}
查看更多
SAY GOODBYE
5楼-- · 2019-09-18 14:51

This code may have a race condition. The magnitude of the problem will depend on exactly what the compiler emits when it compiles this code.

Here's what might be happening. Your code first checks bool_variable == True, which evaluates false. Execution skips the first block and jumps to the else if. Your code then checks bool_variable == False, which also evaluates false so you fall into the final else. You are doing two discrete tests on bool_variable. Something else (such as another thread or an ISR) may be altering the value of bool_variable during the brief window of time after the first test has run and before the second test.

You can avoid the problem completely by using if (bool == True) {} else {} instead of re-testing for false. That version would only check the value once, eliminating the window where corruption can happen. The separate False check doesn't really buy you anything in the first place since by definition a one-bit-wide field can only take on two possible values, so !True must be the same as False. Even if you were using a larger boolean type that could technically take on more than two discrete values, you should be using it as if it could only have two (such as 0=false, everything else=True).

This hints at a much larger problem, though. Even with only one variable check instead of two, you have one thread reading the variable and another altering it at practically the same time. The corruption occurring immediately before the True check would possibly still give you erroneous results but be even harder to detect. You need some sort of locking mechanism (mutex, spinlock, etc) to ensure that only one thread is accessing that field at a time.

The only way to prove any of this for certain, though, is to step through it with a debugger or hardware probe and watch the value change between the two tests. If that's not an option, you may be able to de-couple the blocks by changing the else if to if and storing the value of bool_variable before each of the two tests. Any time the two differ, then something external has corrupted your value.

查看更多
登录 后发表回答