Geoffrey1014/SA_Bugs

GCC --Wanalyzer-null-dereference false negative with `*q == 0`

Opened this issue · 5 comments

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

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

int *f(int *q)
{
    __analyzer_eval(q == 0);
    if (q == 0)
    {
        __analyzer_eval(q == 0);
        if (*q == 0)
        {
            printf("Hello World!");
        }
    }
}

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

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

In this case, after entering the first if branch, the result of eval on line 10 is TRUE. At this time, the fact known to the analyzer is that the pointer q is NULL, and then, for *q == 0 on line 11, analyzer does not generate an NPD warning, which is a little odd.

Here is the analysis result of the case. Thank you for taking the time to review this case.

Input:

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

int *f(int *q)
{
    __analyzer_eval(q == 0);
    if (q == 0)
    {
        __analyzer_eval(q == 0);
        if (*q == 0)
        {
            printf("Hello World!");
        }
    }
}

Output:

<source>: In function 'f':
<source>:7:5: warning: UNKNOWN
    7 |     __analyzer_eval(q == 0);
      |     ^~~~~~~~~~~~~~~~~~~~~~~
<source>:10:9: warning: TRUE
   10 |         __analyzer_eval(q == 0);
      |         ^~~~~~~~~~~~~~~~~~~~~~~

CSA correctly gives the NPD warning: https://godbolt.org/z/jP4hv4c6j and does not evaluate those evaluation statements in the second if statement.

Hi, David.I think this case may be a duplicate of Bug 107733 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107733), because I can reduce it to the following case_1. But, I tried to modify this case to the following case_2 (https://godbolt.org/z/qTze3Mh6T). It seems that GCC adds two contradictory conditions (q == 0 and *q == 0) to equivlent classes. I do not think this still counts a duplicate of case_1. But I am not very sure. Could you spare some time to help me understand this case? Maybe I can help to contribute to GCC Static Analyzer. Thanks a lot!

case_1

#include "stdio.h"
int *f(int *q)
{
    if (q == 0)
    {
        *q == 0;
    }
}

case_2

#include "stdint.h"
#include "stdio.h"
#include <stdbool.h>
#include <stdlib.h>

extern void __analyzer_describe ();
extern void __analyzer_eval ();
extern void __analyzer_dump ();
extern void __analyzer_dump_state (const char *name, ...);
extern void __analyzer_dump_region_model ();
extern void __analyzer_dump_exploded_nodes ();


int *f(int *q)
{
    if (q == 0 && *q == 0)
    {
        __analyzer_dump ();
        __analyzer_eval (q == 0);
        __analyzer_eval (*q == 0);
    }
}

output of case_2:

rmodel:
stack depth: 1
  frame (index 0): frame: 'f'@1
clusters within root region
  cluster for: (*INIT_VAL(q_8(D)))
    ESCAPED
m_called_unknown_fn: FALSE
constraint_manager:
  equiv classes:
    ec0: {(int)0 == INIT_VAL((*INIT_VAL(q_8(D)))) == [m_constant]'0'}
    ec1: {(void *)0B == INIT_VAL(q_8(D)) == [m_constant]'0B'}
  constraints:
<source>: In function 'f':
<source>:19:9: warning: TRUE
   19 |         __analyzer_eval (q == 0);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~
<source>:20:9: warning: TRUE
   20 |         __analyzer_eval (*q == 0);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 0

unrelated code has effect
https://godbolt.org/z/n9fjdfnf4