Geoffrey1014 / SA_Bugs

record bugs of static analyzers
3 stars 1 forks source link

GCC --Wanalyzer-null-dereference false positive with `*f = 42` #29

Open 0-0x41 opened 1 year ago

0-0x41 commented 1 year ago

date: 2023-1-5 commit: 8c8ca873216387bc26046615c806b96f0345ff9d args: -O0 -fanalyzer test:

#include "stdio.h"
#include "stdbool.h"
void __analyzer_eval(int);

struct a
{
    int b;
} c()
{
    struct a d = {1};
    int e = 0;
    int *f = (int *)e;

    for (d.b = 0; e == 0; e++)
    {
        __analyzer_eval(false == ((!d.b) && e));
        if ((!d.b) && e)
        {
            __analyzer_eval(false == ((!d.b) && e));
            *f = 42;
        }
    }
}

void main() { c(); }

report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108325 fix: original:

0-0x41 commented 1 year ago

I got a false positive error when compiling the following program with gcc(trunk) -O0 -fanalyzer in https://godbolt.org/z/db7v3PGYe.

In this case, the eval statement in line 16 gives two results, FALSE and UNKNOWN. The UNKNOWN here is a little odd, and then analyzer analyzes the codes inside the if branch, however, these are unreachable codes.

Input:

#include "stdio.h"
#include "stdbool.h"
void __analyzer_eval(int);

struct a
{
    int b;
} c()
{
    struct a d = {1};
    int e = 0;
    int *f = (int *)e;

    for (d.b = 0; e == 0; e++)
    {
        __analyzer_eval(true == ((!d.b) && e));
        if ((!d.b) && e)
        {
            __analyzer_eval(true == ((!d.b) && e));
            *f = 42;
        }
    }
}

void main() { c(); }

Output:

<source>: In function 'c':
<source>:16:9: warning: FALSE
   16 |         __analyzer_eval(true == ((!d.b) && e));
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:16:9: warning: UNKNOWN
<source>:19:13: warning: TRUE
   19 |             __analyzer_eval(true == ((!d.b) && e));
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:16:9: warning: FALSE
   16 |         __analyzer_eval(true == ((!d.b) && e));
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:16:9: warning: FALSE
<source>:16:9: warning: UNKNOWN
<source>:19:13: warning: TRUE
   19 |             __analyzer_eval(true == ((!d.b) && e));
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:16:9: warning: FALSE
   16 |         __analyzer_eval(true == ((!d.b) && e));
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:20:16: warning: dereference of NULL 'f' [CWE-476] [-Wanalyzer-null-dereference]
   20 |             *f = 42;
      |             ~~~^~~~
  'c': events 1-20
    |
    |   12 |     int *f = (int *)e;
    |      |          ^
    |      |          |
    |      |          (1) 'f' is NULL
    |   13 | 
    |   14 |     for (d.b = 0; e == 0; e++)
    |      |                   ~~~~~~  ~~~
    |      |                     |      |
    |      |                     |      (11) ...to here
    |      |                     (2) following 'true' branch (when 'e == 0')...
    |      |                     (12) following 'true' branch (when 'e == 0')...
    |   15 |     {
    |   16 |         __analyzer_eval(true == ((!d.b) && e));
    |      |                                 ~~~~~~~~~~~~~
    |      |                                     |   |
    |      |                                     |   (4) following 'true' branch...
    |      |                                     |   (5) ...to here
    |      |                                     |   (6) following 'false' branch (when 'e == 0')...
    |      |                                     |   (7) ...to here
    |      |                                     |   (14) following 'true' branch...
    |      |                                     |   (15) ...to here
    |      |                                     (3) ...to here
    |      |                                     (13) ...to here
    |   17 |         if ((!d.b) && e)
    |      |            ~~~~~~~~~~~~
    |      |            |       |
    |      |            |       (9) ...to here
    |      |            |       (10) following 'false' branch (when 'e == 0')...
    |      |            |       (17) ...to here
    |      |            (8) following 'true' branch...
    |      |            (16) following 'true' branch...
    |   18 |         {
    |   19 |             __analyzer_eval(true == ((!d.b) && e));
    |      |                                     ~~~~~~~~~~~~~
    |      |                                             |
    |      |                                             (18) following 'true' branch...
    |      |                                             (19) ...to here
    |   20 |             *f = 42;
    |      |             ~~~~~~~
    |      |                |
    |      |                (20) dereference of NULL 'f'
    |

I set it directly to 0 in the initialization of d.b, and then keep the semantics of the for loop executing only one time, and after making the following transformation (https://godbolt.org/z/nvePK1sdb), the NPD warning disappeared, and the eval statement in line 19 is not output.

Thank you for taking the time to review these cases.

Geoffrey1014 commented 1 year ago

The path notes show that GSA does not really check the conditon of for loop. It seems that GSA does not update iterator

New method should could also find this bug.

Geoffrey1014 commented 1 year ago

It seems that GSA has a problem when unrolling .

image

https://godbolt.org/z/3KsePje8a

Geoffrey1014 commented 1 year ago

CSA : https://godbolt.org/z/1bjMGsrjx

ghost commented 1 year ago

CSA not FP: https://godbolt.org/z/bsTr67fv1