[comp.windows.x] Bug in _XtFindRemapWidget ?

alan@metasoft.UUCP (Alan Epstein) (10/05/90)

Recently our application began crashing reliably during a mouse
down event in a small widget hierarchy. We traced the problem
to _XtFindRemapWidget, which makes a preliminary test to
determine whether the pdi->trace array should be refilled (by
calling _XtFillAncestorList). In some cases where it decides
not to refill the array, the existing array contains garbage,
causing a crash later when _XtGetPerWidgetInput tries to
dereference through the invalid widget.

The bug appears to be due to the possibility that Xt reassigns
widget id's, ie: after a widget is destroyed (and presumably
its id is freed), XtCreate might return the same id.
This new id is valid, but the result is that the test in
_XtFindRemapWidget might succeed (ie: widget == pdi->trace[0])
when the other elements in the pdi->trace array are old and obsolete.

The fix I am trying involves merely removing the test at the
head of _XtFindRemapWidget, causing _XtFillAncestorList to
make the array right every time. This seems like little overhead
since the latter routine doesn't need to do much.

I don't profess to be an expert in the Xt code, so I would
appreciate a confirmation by someone who is.

-----------------------------
Alan Epstein
Meta Software Corp                   UUCP:  ...bbn!metasoft!alan
150 Cambridgepark Dr        Internet/ARPA:  alan%metasoft@bbn.com
Cambridge, MA 02140  USA              TEL:  (617) 576.6920
-----------------------------

alan@metasoft.UUCP (Alan Epstein) (10/08/90)

In article <1874@metasoft.UUCP>, alan@metasoft.UUCP (Alan Epstein) writes:
> Recently our application began crashing reliably during a mouse down...

I just looked at patches 15-17 and noticed this had already been fixed.

Never Mind!....

Please send replies via e-mail. Avoid net clutter!

Thanks.

-----------------------------
Alan Epstein
Meta Software Corp                   UUCP:  ...bbn!metasoft!alan
150 Cambridgepark Dr        Internet/ARPA:  alan%metasoft@bbn.com
Cambridge, MA 02140  USA
Cambridge, MA 02140  USA              IAP:  ILS 11 BED
Cambridge, MA 02140  USA              TEL:  (617) 576.6920
-----------------------------

gourley@CAD.MCC.COM (Kevin Gourley) (10/08/90)

Alan, you ran into the same bug that I had experienced over a month
ago.  I sent out a message to xpert about this bug and I later
sent it in as a bug report with suggested patch to xbug.  I have
included my original message below.  After a month of testing, 
my patch seems to have fixed the problem!

We recently applied the latest MIT patches.  After looking into
the code, it appears to me that they also have fixed the problem.

You are right.  You probably DON'T want to call _XtFillAncestorList
every time.  You probablly DO want to apply the latest patches though.

Kevin Gourley (gourley@mcc.com) 512-338-3589
Microelectronics and Computer Technology Corp., Austin, TX


       My Original message about this bug is below
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

I have been experiencing a problem that has been causing 'bus error's
frequently since we started using GNU malloc.  I finally tracked it down
to a bug in Xt.  The use of GNU malloc only makes the bug more apparent.

The problem is in EventUtil.c in _XtFindRemapWidget:

Widget _XtFindRemapWidget(event, widget, mask, pdi)
    XEvent	*event;
    Widget	widget;
    EventMask	mask;
    XtPerDisplayInput pdi;
{
    Widget		dspWidget = widget;
    
+->  if (!pdi->traceDepth || !(widget == pdi->trace[0]))
|     {
|	  _XtFillAncestorList(&pdi->trace, &pdi->traceMax, 
|			      &pdi->traceDepth, widget, NULL);
|	  pdi->focusWidget = NULL; /* invalidate the focus
|				      cache */
|      }
|    ...
|
|
This 'if' condition leads to erroneous results.  pdi retains
a cached snapshot of the path up the widget hierarchy in
pdi->trace (which is an array of widget pointers).  These values are
recomputed by calling _XtFillAncestorList whenever it is
determined that the cache is no longer valid.  The condition in
the 'if' statement is NOT SUFFICIENT to adequately check to see
if the pdi->trace array is still valid.

I had some code in a rather complex application that created a dialog
box composed of a hierarchy of widgets.  Upon interacting with that
dialog, values would get cached in the pdi->trace array.  Then at some
later point, I would end up destroying the widgets and their memory
would get 'free'd.  Later, when I created a new dialog composed of
freshly allocated widgets, I would occasionally encounter cases where
some of the new widgets coincided in memory location with the
addresses of widgets in the previous dialog.  (This was a result of a
smarter memory allocator).  The problem is that even though I have a
new hierarchy of widgets, the '(widget == pdi->trace[0])' test was
erroneous because sometimes the widget pointer WAS indeed the same as
the cached widget pointer value in pdi->trace[0] BUT it was not the
SAME widget.  Since the time that the widget pointer value was cached,
that widget was freed and a new widget was allocated in the same
space.  That was the only thing that was the same though!  The rest of
the pdi->trace array did not reflect the current path up the widget
hierarchy and in fact contained stale pointers to widgets that had
been freed.  Since the 'if' clause did not detect the erroneous
pdi->trace array, it did not recompute the values by calling
_XtFillAncestorList and it resulted in 'bus errors' further down in
the code.

This doesn't appear to be as much of a problem in other forms of
malloc because new widgets don't tend to get allocated in the exact
same location as older freed widgets.

My solution is the following:

1) I created a function in EventUtil.c that resets the pdi->traceDepth
   to 0, thus causing the pdi->trace array to be recomputed when necessary.

     void _XtClearPerDisplayInputCache(dpy)
     Display       *dpy;
     {
         XtPerDisplayInput pdi;
    
         pdi = _XtGetPerDisplayInput(dpy);
	 if (pdi) pdi->traceDepth = 0;
     }

2) I changed a function in Destroy.c that now calls
   _XtClearPerDisplayInputCache whenever a widget is desroyed to mark
   the cache as "dirty".

      static void Phase1Destroy (widget)
      Widget    widget;
      {
>         extern void _XtClearPerDisplayInputCache();
>         Screen  *screen;
      
          widget->core.being_destroyed = TRUE;
      
>          if (screen = XtScreenOfObject(widget))
>              _XtClearPerDisplayInputCache(DisplayOfScreen(screen));

      } /* Phase1Destroy */


Please let me know if this has been fixed by a patch or if my solution is
not correct.  Thank you.
     /                      ____              
    /__/ ___      o ___    /     ___           / ___ 
   /\   /__/ | / / /  /   / --- /  / /  / /-- / /__/ | /
  /  | /__   |/ / /  /   /___/ /__/ /__/ /   / /__   |/
  ___________________________________________________/
  Microelectronics & Computer Tech.Corp.(MCC), CAD Dept.
  3500 W.Balcones Center Dr., Austin,TX   78759                        
  gourley@mcc.com  (512)338-3589      FAX:(512)338-3600

----------------------------------
In 'diff' form, the patches are:

Xt/EventUtil.c:
74a75,84
> void _XtClearPerDisplayInputCache(dpy)
> Display       *dpy;
> {
>     XtPerDisplayInput pdi;
>
>     pdi = _XtGetPerDisplayInput(dpy);
>     if (pdi) pdi->traceDepth = 0;
> }
>
>

Xt/Destroy.c:
59a60,62
>     extern void _XtClearPerDisplayInputCache();
>     Screen  *screen;
>
60a64,67
>
>     if (screen = XtScreen(widget))
>       _XtClearPerDisplayInputCache(DisplayOfScreen(screen));
>
----------------------------------