https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101134

--- Comment #12 from Martin Sebor <msebor at gcc dot gnu.org> ---
I don't need to be convinced that it would be nice to be able to differentiate
between certain bugs and possible ones.  The text of this class of warnings
already does differentiate between "may write/read/access" and
"writing/reading/accessing" under some conditions:

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/builtins.c;h=73c12e3bb8c39907b6bd95148f860709dbbf3f50;hb=refs/heads/releases/gcc-11#l4136

This is not among them.  In this case the IL that triggers the warning is:

  <bb 3> [local count: 10145877954]:
  # size_18 = PHI <512(2), size_13(10)>
  # prephitmp_54 = PHI <&quotedFunction.staticBuf(2), newBuf_36(10)>
  ...
  <bb 7> [local count: 2825037180]:
  newBuf_33 = malloc (_51);
  goto <bb 9>; [100.00%]

  <bb 8> [local count: 6591753510]:
  newBuf_35 = realloc (_30, _51);
  ...
  <bb 9> [local count: 9416790700]:
  # newBuf_36 = PHI <newBuf_33(7), newBuf_35(8)>
  ...
  <bb 14> [local count: 3449598541]:
  MEM[(char *)prephitmp_54 + -1B] = 0;   <<< -Wstring-overflow

prephitmp_54 is set to point to either the beginning of
quotedFunction.staticBuf or the dynamically allocated buffer.  So prephitmp_54
- 1 is unconditionally invalid and the warning triggers.  The warning doesn't
consider the predicates leading up to the invalid store: all it uses to make
its decision is the statement itself and its arguments.  For PHIs, to minimize
noise, it triggers only if the statement is invalid for all arguments.  This is
how most flow-based warnings work in GCC (some like -Warray-bounds don't
consider PHIs yet).

To do better and either hope to issue a "maybe" kind of a warning or preferably
avoid it altogether if the code is unreachable we would need to do what
-Wmaybe-uninitialized does (as I said in comment #5) and analyze the
predicates.  I've been working on extracting the uninitialized predicate
analyzer for the last few months.  I'm not sure to what extent it will be
usable outside the uninitialized pass yet or how well it will work.  As we
know, -Wmaybe-uninitialized has lots of false positives (and negatives).  But
even the uninitialized pass issues unconditional warnings for conditional bugs.
 For instance:

  int f (void)
  { 
    int i, j = 0;
    int *p = i ? &i : &j;
    return *p;
  }

a.c: In function ā€˜f’:
a.c:4:14: warning: ā€˜i’ is used uninitialized [-Wuninitialized]
    4 |   int *p = i ? &i : &j;
      |              ^

It does that because the first time it runs it's too early to make the
distinction, and by the second time it runs to issuse -Wmaybe-uninitialized the
uninitialized read has been eliminated.  And this is done to strike a
reasonable balance between false negatives and positives.

So in general, the distinction between the two cases can only be made when it
can be discerned from the IL, and the IL doesn't always preserve the
conditional nature of the problem statement.  So every warning must always be
viewed as a "maybe" kind of a warning.  It will never be a sure a thing, either
at the scope of individual functions, and certainly not with inlining or
function cloning.

Reply via email to