Allow X*IfEvent() to reenter libX11

The documentation for this family of functions very clearly says not to
call into xlib in your predicate function, but historically single
threaded apps could get away with it just fine, and now that we've
forced thread-safety on the world such apps will now deadlock instead.
That's not an acceptable regression even if the app is technically
broken. This has been reported with XFCE and FVWM, and Motif's
cut-and-paste code has the same bug by inspection, so this does need to
be addressed.

This change nerfs LockDisplay/UnlockDisplay while inside the critical
bit of an IfEvent function. This is still safe in the sense that the
display remains locked and no other thread should be able to change it
from under us, but the loop that scans the event queue might not be
robust against it being modified as a side effect of protocol emitted by
the predicate callback. But that's not new, non-XInitThreads'd xlib
would have the same caveat.

Closes: xorg/lib/libx11#157
This commit is contained in:
Adam Jackson 2022-08-05 15:19:08 -04:00 committed by Adam Jackson
parent 0d1d65bdd9
commit 7977557541
6 changed files with 39 additions and 0 deletions

View file

@ -207,6 +207,7 @@ struct _XDisplay
XIOErrorExitHandler exit_handler;
void *exit_handler_data;
Bool in_ifevent;
};
#define XAllocIDs(dpy,ids,n) (*(dpy)->idlist_alloc)(dpy,ids,n)

View file

@ -50,6 +50,7 @@ Bool XCheckIfEvent (
int n; /* time through count */
LockDisplay(dpy);
dpy->in_ifevent = True;
prev = NULL;
for (n = 3; --n >= 0;) {
for (qelt = prev ? prev->next : dpy->head;
@ -60,6 +61,7 @@ Bool XCheckIfEvent (
*event = qelt->event;
_XDeq(dpy, prev, qelt);
_XStoreEventCookie(dpy, event);
dpy->in_ifevent = False;
UnlockDisplay(dpy);
return True;
}
@ -78,6 +80,7 @@ Bool XCheckIfEvent (
/* another thread has snatched this event */
prev = NULL;
}
dpy->in_ifevent = False;
UnlockDisplay(dpy);
return False;
}

View file

@ -49,6 +49,7 @@ XIfEvent (
unsigned long qe_serial = 0;
LockDisplay(dpy);
dpy->in_ifevent = True;
prev = NULL;
while (1) {
for (qelt = prev ? prev->next : dpy->head;
@ -59,6 +60,7 @@ XIfEvent (
*event = qelt->event;
_XDeq(dpy, prev, qelt);
_XStoreEventCookie(dpy, event);
dpy->in_ifevent = False;
UnlockDisplay(dpy);
return 0;
}

View file

@ -189,6 +189,7 @@ XOpenDisplay (
dpy->xcmisc_opcode = 0;
dpy->xkb_info = NULL;
dpy->exit_handler_data = NULL;
dpy->in_ifevent = False;
/*
* Setup other information in this display structure.

View file

@ -50,6 +50,7 @@ XPeekIfEvent (
unsigned long qe_serial = 0;
LockDisplay(dpy);
dpy->in_ifevent = True;
prev = NULL;
while (1) {
for (qelt = prev ? prev->next : dpy->head;
@ -63,6 +64,7 @@ XPeekIfEvent (
_XStoreEventCookie(dpy, &copy);
*event = copy;
}
dpy->in_ifevent = False;
UnlockDisplay(dpy);
return 0;
}

View file

@ -452,6 +452,32 @@ static void _XDisplayLockWait(
}
}
static void _XLockDisplay(
Display *dpy
XTHREADS_FILE_LINE_ARGS
);
static void _XIfEventLockDisplay(
Display *dpy
XTHREADS_FILE_LINE_ARGS
)
{
/* assert(dpy->in_ifevent); */
}
static void _XIfEventUnlockDisplay(
Display *dpy
XTHREADS_FILE_LINE_ARGS
)
{
if (dpy->in_ifevent)
return;
dpy->lock_fns->lock_display = _XLockDisplay;
dpy->lock_fns->unlock_display = _XUnlockDisplay;
UnlockDisplay(dpy);
}
static void _XLockDisplay(
Display *dpy
XTHREADS_FILE_LINE_ARGS
@ -478,6 +504,10 @@ static void _XLockDisplay(
#endif
_XIDHandler(dpy);
_XSeqSyncFunction(dpy);
if (dpy->in_ifevent) {
dpy->lock_fns->lock_display = _XIfEventLockDisplay;
dpy->lock_fns->unlock_display = _XIfEventUnlockDisplay;
}
}
/*