On Mon, 2022-02-14 at 09:26 -0700, Jeff Law wrote:
> 
> 
> On 2/14/2022 8:57 AM, David Malcolm via Gcc wrote:
> > [CCing Mark in the hopes of insight from the valgrind side of things]
> > 
> > There is a false positive from -Wanalyzer-use-of-uninitialized-value
> > on
> > gcc.dg/analyzer/pr102692.c here:
> > 
> >    ‘fix_overlays_before’: events 1-3
> >      |
> >      |   75 |   while (tail
> >      |      |          ~~~~
> >      |   76 |          && (tem = make_lisp_ptr (tail, 5),
> >      |      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >      |      |          |
> >      |      |          (1) following ‘false’ branch (when ‘tail’ is
> > NULL)...
> >      |   77 |              (end = marker_position (XOVERLAY (tem)-
> > >end)) >= pos))
> >      |      |             
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >      |......
> >      |   82 |   if (!tail || end < prev || !tail->next)
> >      |      |       ~~~~~    ~~~~~~~~~~
> >      |      |       |            |
> >      |      |       |            (3) use of uninitialized value ‘end’
> > here
> >      |      |       (2) ...to here
> >      |
> > 
> > The issue is that inner || of the conditionals have been folded
> > within the
> > frontend from a chain of control flow:
> > 
> >     5   │   if (tail == 0B) goto <D.1986>; else goto <D.1988>;
> >     6   │   <D.1988>:
> >     7   │   if (end < prev) goto <D.1986>; else goto <D.1989>;
> >     8   │   <D.1989>:
> >     9   │   _1 = tail->next;
> >    10   │   if (_1 == 0B) goto <D.1986>; else goto <D.1987>;
> >    11   │   <D.1986>:
> > 
> > to an OR expr (and then to a bitwise-or by the gimplifier):
> > 
> >     5   │   _1 = tail == 0B;
> >     6   │   _2 = end < prev;
> >     7   │   _3 = _1 | _2;
> >     8   │   if (_3 != 0) goto <D.1986>; else goto <D.1988>;
> >     9   │   <D.1988>:
> >    10   │   _4 = tail->next;
> >    11   │   if (_4 == 0B) goto <D.1986>; else goto <D.1987>;
> > 
> > This happens for sufficiently simple conditionals in
> > fold_truth_andor.
> > In particular, the (end < prev) is short-circuited without
> > optimization,
> > but is evaluated with optimization, leading to the false positive.
> > 
> > Given how early this folding occurs, it seems the simplest fix is to
> > try to detect places where this optimization appears to have
> > happened,
> > and suppress uninit warnings within the statement that would have
> > been short-circuited (and thus e.g. ignoring them when evaluating _2
> > above for the case where _1 is known to be true at the (_1 | _2) ,
> > and
> > thus _2 being redundant).
> > 
> > Attached is a patch that implements this.
> > 
> > There are some more details in the patch, but I'm wondering if this
> > is a
> > known problem, and how e.g. valgrind copes with such code.  My patch
> > feels like something of a hack, but I'm not sure of any other way
> > around
> > it given that the conditional is folded directly within the frontend.
> Presumably when "tail ==0", "end" is initialized somewhere?  

I don't think it does, but it shouldn't be a problem in the code as
written, due to short-circuiting (as I understand things) - but the
short-circuiting is being removed by the optimizer.

"end" only gets initialized at line 71 of the source below, for the
case where the initial value of "tail" is non-NULL:

  63   │ void
  64   │ fix_overlays_before (struct buffer *bp, long prev, long pos)
  65   │ {
  66   │   struct Lisp_Overlay *tail = bp->overlays_before, *parent = 0, 
*right_pair;
  67   │   struct lisp *tem;
  68   │   long end;
  69   │   while (tail
  70   │      && (tem = make_lisp_ptr (tail, 5),
  71   │          (end = marker_position (XOVERLAY (tem)->end)) >= pos))
  72   │     {
  73   │       parent = tail;
  74   │       tail = tail->next;
  75   │     }
  76   │   if (!tail || end < prev || !tail->next) /* { dg-bogus "use of 
uninitialized value 'end'" "uninit" } */
  77   │     /* { dg-bogus "dereference of NULL 'tail'" "null deref" { target 
*-*-* } .-1 } */
  78   │     return;

At -O2 we have this at pr102692.c.022t.ssa:

  59   │   <bb 2> :
  60   │   tail_23 = bp_22(D)->overlays_before;
  61   │   parent_24 = 0B;
  62   │   goto <bb 4>; [INV]
  63   │ 
  64   │   <bb 3> :
  65   │   parent_32 = tail_11;
  66   │   tail_33 = tail_11->next;
  67   │ 
  68   │   <bb 4> :
  69   │   # tail_11 = PHI <tail_23(2), tail_33(3)>
  70   │   # parent_13 = PHI <parent_24(2), parent_32(3)>
  71   │   # end_15 = PHI <end_25(D)(2), end_30(3)>
  72   │   if (tail_11 != 0B)
  73   │     goto <bb 5>; [INV]
  74   │   else
  75   │     goto <bb 6>; [INV]
  76   │ 
  77   │   <bb 5> :
  78   │   tem_27 = make_lisp_ptr (tail_11, 5);
  79   │   _1 = XOVERLAY (tem_27);
  80   │   _2 = _1->end;
  81   │   end_30 = marker_position (_2);
  82   │   if (end_30 >= pos_31(D))
  83   │     goto <bb 3>; [INV]
  84   │   else
  85   │     goto <bb 6>; [INV]
  86   │ 
  87   │   <bb 6> :
  88   │   # end_16 = PHI <end_15(4), end_30(5)>
  89   │   _3 = tail_11 == 0B;
  90   │   _4 = end_16 < prev_34(D);
  91   │   _5 = _3 | _4;
  92   │   if (_5 != 0)
  93   │     goto <bb 8>; [INV]
  94   │   else
  95   │     goto <bb 7>; [INV]
  96   │ 
  97   │   <bb 7> :
  98   │   _6 = tail_11->next;
  99   │   if (_6 == 0B)
 100   │     goto <bb 8>; [INV]
 101   │   else
 102   │     goto <bb 9>; [INV]
 103   │ 


This line of the source:
  76   │   if (!tail || end < prev || !tail->next) 
has become BBs 6 and 7.

Consider the path:
  ENTRY -> BB 2 -> BB 4 -> BB 6 (initial tail being NULL)

If initially tail_23 is NULL, then:
* at BB 2 -> BB 4 we have tail_11 = tail_23 and end_15 = end_25(D)
* at BB 4 -> BB 6 we have end_16 = end_15 = end_25(D)

and so we have:

  89   │   _3 = tail_11 == 0B;

with tail_11 being NULL, and thus we know _3 is true on this path, and
then:

  90   │   _4 = end_16 < prev_34(D);

where end_16 is end_25(D), the "initial" value of "end" - but "end"
isn't initialized.

So we have: _4 = UNINITIALIZED < VALID;  (the 2nd arg, prev is a param)
which is an evaluation using an uninitialized value.

However _4 only gets used by:

  91   │   _5 = _3 | _4;

all boolean, with _3 known to be true.

Assuming I'm reading all this correctly, this seems like overzealous
folding to me, and my special-casing of it - the folding turns a
TRUTH_OR_EXPR into a TRUTH_OR, given that the arguments
are simple_operand_p_2, which seems to apply for the simplest lookups
of local variables, without side-effects.

tree.def says:

  /* ANDIF and ORIF allow the second operand not to be computed if the
     value of the expression is determined from the first operand.  AND,
     OR, and XOR always compute the second operand whether its value is
     needed or not (for side effects).  The operand may have

and presumably the "allow the 2nd operand not to be computed" is not
the same as "require the 2nd operand not to be computed".

Does the above reasoning (and my special-case workaround) sound correct
to you?

Thanks!
Dave



> If so, yes, 
> this is a known issue.  There's a BZ about it somewhere (I don' t have 
> the # handy, but it's probably on the Wuninitialized tracker).
> 
> 
> Jeff
> 


Reply via email to