Geoffrey1014 / SA_Bugs

record bugs of static analyzers
2 stars 1 forks source link

GCC --Wanalyzer-null-dereference false positive with `**m = 1` #20

Closed 0-0x41 closed 1 year ago

0-0x41 commented 1 year ago

date: 2022-12-12 commit: 8c8ca873216387bc26046615c806b96f0345ff9d args: -O2 -fanalyzer test:

#include "stdio.h"
int main()
{
    int *l[2];
    int **m = l;
    for (int e = 0; e < 2; e++)
        l[e] = (int *)42;
    __analyzer_eval(*m);
    (*m == 0) && (__analyzer_eval(0 == *m), **m = 1);
}

report: fix: original:

0-0x41 commented 1 year ago

I got a false positive error when compiling the following program with gcc(trunk) -fanalyzer -O2 in https://godbolt.org/z/jrxKfKEzG. Here is the result of its analysis, please take a look, thanks a lot.

#include "stdio.h"
int main()
{
    int *l[2];
    int **m = l;
    for (int e = 0; e < 2; e++)
        l[e] = (int *)42;
    __analyzer_eval(0 == *m);
    (*m == 0) && (__analyzer_eval(0 == *m), **m = 1);
}
<source>: In function 'main':
<source>:8:5: warning: UNKNOWN
    8 |     __analyzer_eval(0 == *m);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~
<source>:9:19: warning: UNKNOWN
    9 |     (*m == 0) && (__analyzer_eval(0 == *m), **m = 1);
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~
<source>:9:49: warning: dereference of NULL '0' [CWE-476] [-Wanalyzer-null-dereference]
    9 |     (*m == 0) && (__analyzer_eval(0 == *m), **m = 1);
      |                                             ~~~~^~~
  'main': events 1-5
    |
    |    6 |     for (int e = 0; e < 2; e++)
    |      |                     ~~^~~
    |      |                       |
    |      |                       (1) following 'true' branch (when 'e != 2')...
    |    7 |         l[e] = (int *)42;
    |      |         ~              
    |      |         |
    |      |         (2) ...to here
    |    8 |     __analyzer_eval(0 == *m);
    |    9 |     (*m == 0) && (__analyzer_eval(0 == *m), **m = 1);
    |      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |               |   |                             |
    |      |               |   |                             (5) dereference of NULL '0'
    |      |               |   (4) ...to here
    |      |               (3) following 'true' branch...

In this case, the result of *m == 0 is FALSE, and the statement after && is unreachable code, which probably should not generate an NPD warning. If the initialization is done while the pointer array l is defined, the NPD warning will not be generated (like this, https://godbolt.org/z/5hPhj1M8q) . __analyzer_eval(0 == *m) on line 8 results in FALSE, not UNKNOWN, and __analyzer_eval(0 == *m) on line 9 has no output (unreachable code). I think this may be a combination problem about modeling pointer arrays and initializing them under a for loop.

Still for this case, its __analyzer_eval(0 == *m) at -O0, line 8 and line 9 have the same result as -O2 without generating the NPD warning.

Also, I found a little bit of a problem, please take a look. In this case (https://godbolt.org/z/Mh6acd66v), the result of __analyzer_eval(*m) is UNKNOWN, yet the result of __analyzer_eval(0 == *m) with __analyzer_eval(*m == 0) is FALSE.

Geoffrey1014 commented 1 year ago

The difference between O0 and O2 is that O0 knows "m_16 == &n" while O2 does not know.

However, GSA does not know what is in the array n and enters the true branch of "if (*m == 0)", which should be fixed. CSA does a better job.

-O0: https://godbolt.org/z/5zKKcrf5c

image

-O2: https://godbolt.org/z/f1GnMhrqq

image
Geoffrey1014 commented 1 year ago

duplicate of issue https://github.com/Geoffrey1014/SA_Bugs/issues/16