|
|
936a6c |
From 8c92ef59890c6d6e2be456658d3b9c145eda8629 Mon Sep 17 00:00:00 2001
|
|
|
936a6c |
From: Keith Packard <keithp@keithp.com>
|
|
|
936a6c |
Date: Sat, 7 Nov 2020 22:22:47 -0800
|
|
|
936a6c |
Subject: [PATCH libX11] Avoid recursing through _XError due to sequence
|
|
|
936a6c |
adjustment
|
|
|
936a6c |
|
|
|
936a6c |
This patch is based on research done by Dmitry Osipenko to uncover the
|
|
|
936a6c |
cause of a large class of Xlib lockups.
|
|
|
936a6c |
|
|
|
936a6c |
_XError must unlock and re-lock the display around the call to the
|
|
|
936a6c |
user error handler function. When re-locking the display, two
|
|
|
936a6c |
functions are called to ensure that the display is ready to generate a request:
|
|
|
936a6c |
|
|
|
936a6c |
_XIDHandler(dpy);
|
|
|
936a6c |
_XSeqSyncFunction(dpy);
|
|
|
936a6c |
|
|
|
936a6c |
The first ensures that there is at least one XID available to use
|
|
|
936a6c |
(possibly calling _xcb_generate_id to do so). The second makes sure a
|
|
|
936a6c |
reply is received at least every 65535 requests to keep sequence
|
|
|
936a6c |
numbers in sync (possibly generating a GetInputFocus request and
|
|
|
936a6c |
synchronously awaiting the reply).
|
|
|
936a6c |
|
|
|
936a6c |
If the second of these does generate a GetInputFocus request and wait
|
|
|
936a6c |
for the reply, then a pending error will cause recursion into _XError,
|
|
|
936a6c |
which deadlocks the display.
|
|
|
936a6c |
|
|
|
936a6c |
One seemingly easy fix is to have _XError avoid those calls by
|
|
|
936a6c |
invoking InternalLockDisplay instead of LockDisplay. That function
|
|
|
936a6c |
does everything that LockDisplay does *except* call those final two
|
|
|
936a6c |
functions which may end up receiving an error.
|
|
|
936a6c |
|
|
|
936a6c |
However, that doesn't protect the system from applications which call
|
|
|
936a6c |
some legal Xlib function from within their error handler. Any Xlib
|
|
|
936a6c |
function which cannot generate protocol or wait for events is valid,
|
|
|
936a6c |
including many which invoke LockDisplay.
|
|
|
936a6c |
|
|
|
936a6c |
What we need to do is make LockDisplay skip these two function calls
|
|
|
936a6c |
precisely when it is called from within the _XError context for the
|
|
|
936a6c |
same display.
|
|
|
936a6c |
|
|
|
936a6c |
This patch accomplishes this by creating a list of threads in the
|
|
|
936a6c |
display which are in _XError, and then having LockDisplay check the
|
|
|
936a6c |
current thread against those list elements.
|
|
|
936a6c |
|
|
|
936a6c |
Inspired-by: Dmitry Osipenko <digetx@gmail.com>
|
|
|
936a6c |
Signed-off-by: Keith Packard <keithp@keithp.com>
|
|
|
936a6c |
Tested-by: Dmitry Osipenko <digetx@gmail.com>
|
|
|
936a6c |
Reviewed-by: Dmitry Osipenko <digetx@gmail.com>
|
|
|
936a6c |
(cherry picked from commit 30ccef3a48029bf4fc31d4abda2d2778d0ad6277)
|
|
|
936a6c |
---
|
|
|
936a6c |
include/X11/Xlibint.h | 2 ++
|
|
|
936a6c |
src/OpenDis.c | 1 +
|
|
|
936a6c |
src/XlibInt.c | 10 ++++++++++
|
|
|
936a6c |
src/locking.c | 12 ++++++++++++
|
|
|
936a6c |
src/locking.h | 12 ++++++++++++
|
|
|
936a6c |
5 files changed, 37 insertions(+)
|
|
|
936a6c |
|
|
|
936a6c |
diff --git a/include/X11/Xlibint.h b/include/X11/Xlibint.h
|
|
|
936a6c |
index 6b95bcf7..09078e3f 100644
|
|
|
936a6c |
--- a/include/X11/Xlibint.h
|
|
|
936a6c |
+++ b/include/X11/Xlibint.h
|
|
|
936a6c |
@@ -202,6 +202,8 @@ struct _XDisplay
|
|
|
936a6c |
unsigned long last_request_read_upper32bit;
|
|
|
936a6c |
unsigned long request_upper32bit;
|
|
|
936a6c |
#endif
|
|
|
936a6c |
+
|
|
|
936a6c |
+ struct _XErrorThreadInfo *error_threads;
|
|
|
936a6c |
};
|
|
|
936a6c |
|
|
|
936a6c |
#define XAllocIDs(dpy,ids,n) (*(dpy)->idlist_alloc)(dpy,ids,n)
|
|
|
936a6c |
diff --git a/src/OpenDis.c b/src/OpenDis.c
|
|
|
936a6c |
index 82723578..85901168 100644
|
|
|
936a6c |
--- a/src/OpenDis.c
|
|
|
936a6c |
+++ b/src/OpenDis.c
|
|
|
936a6c |
@@ -201,6 +201,7 @@ XOpenDisplay (
|
|
|
936a6c |
X_DPY_SET_LAST_REQUEST_READ(dpy, 0);
|
|
|
936a6c |
dpy->default_screen = iscreen; /* Value returned by ConnectDisplay */
|
|
|
936a6c |
dpy->last_req = (char *)&_dummy_request;
|
|
|
936a6c |
+ dpy->error_threads = NULL;
|
|
|
936a6c |
|
|
|
936a6c |
/* Initialize the display lock */
|
|
|
936a6c |
if (InitDisplayLock(dpy) != 0) {
|
|
|
936a6c |
diff --git a/src/XlibInt.c b/src/XlibInt.c
|
|
|
936a6c |
index 4e45e62b..8771b791 100644
|
|
|
936a6c |
--- a/src/XlibInt.c
|
|
|
936a6c |
+++ b/src/XlibInt.c
|
|
|
936a6c |
@@ -1482,6 +1482,11 @@ int _XError (
|
|
|
936a6c |
if (_XErrorFunction != NULL) {
|
|
|
936a6c |
int rtn_val;
|
|
|
936a6c |
#ifdef XTHREADS
|
|
|
936a6c |
+ struct _XErrorThreadInfo thread_info = {
|
|
|
936a6c |
+ .error_thread = xthread_self(),
|
|
|
936a6c |
+ .next = dpy->error_threads
|
|
|
936a6c |
+ }, **prev;
|
|
|
936a6c |
+ dpy->error_threads = &thread_info;
|
|
|
936a6c |
if (dpy->lock)
|
|
|
936a6c |
(*dpy->lock->user_lock_display)(dpy);
|
|
|
936a6c |
UnlockDisplay(dpy);
|
|
|
936a6c |
@@ -1491,6 +1496,11 @@ int _XError (
|
|
|
936a6c |
LockDisplay(dpy);
|
|
|
936a6c |
if (dpy->lock)
|
|
|
936a6c |
(*dpy->lock->user_unlock_display)(dpy);
|
|
|
936a6c |
+
|
|
|
936a6c |
+ /* unlink thread_info from the list */
|
|
|
936a6c |
+ for (prev = &dpy->error_threads; *prev != &thread_info; prev = &(*prev)->next)
|
|
|
936a6c |
+ ;
|
|
|
936a6c |
+ *prev = thread_info.next;
|
|
|
936a6c |
#endif
|
|
|
936a6c |
return rtn_val;
|
|
|
936a6c |
} else {
|
|
|
936a6c |
diff --git a/src/locking.c b/src/locking.c
|
|
|
936a6c |
index 9f4fe067..bcadc857 100644
|
|
|
936a6c |
--- a/src/locking.c
|
|
|
936a6c |
+++ b/src/locking.c
|
|
|
936a6c |
@@ -453,6 +453,9 @@ static void _XLockDisplay(
|
|
|
936a6c |
XTHREADS_FILE_LINE_ARGS
|
|
|
936a6c |
)
|
|
|
936a6c |
{
|
|
|
936a6c |
+#ifdef XTHREADS
|
|
|
936a6c |
+ struct _XErrorThreadInfo *ti;
|
|
|
936a6c |
+#endif
|
|
|
936a6c |
#ifdef XTHREADS_WARN
|
|
|
936a6c |
_XLockDisplayWarn(dpy, file, line);
|
|
|
936a6c |
#else
|
|
|
936a6c |
@@ -460,6 +463,15 @@ static void _XLockDisplay(
|
|
|
936a6c |
#endif
|
|
|
936a6c |
if (dpy->lock->locking_level > 0)
|
|
|
936a6c |
_XDisplayLockWait(dpy);
|
|
|
936a6c |
+#ifdef XTHREADS
|
|
|
936a6c |
+ /*
|
|
|
936a6c |
+ * Skip the two function calls below which may generate requests
|
|
|
936a6c |
+ * when LockDisplay is called from within _XError.
|
|
|
936a6c |
+ */
|
|
|
936a6c |
+ for (ti = dpy->error_threads; ti; ti = ti->next)
|
|
|
936a6c |
+ if (ti->error_thread == xthread_self())
|
|
|
936a6c |
+ return;
|
|
|
936a6c |
+#endif
|
|
|
936a6c |
_XIDHandler(dpy);
|
|
|
936a6c |
_XSeqSyncFunction(dpy);
|
|
|
936a6c |
}
|
|
|
936a6c |
diff --git a/src/locking.h b/src/locking.h
|
|
|
936a6c |
index 5251a60c..59fc866e 100644
|
|
|
936a6c |
--- a/src/locking.h
|
|
|
936a6c |
+++ b/src/locking.h
|
|
|
936a6c |
@@ -149,6 +149,18 @@ typedef struct _LockInfoRec {
|
|
|
936a6c |
xmutex_t lock;
|
|
|
936a6c |
} LockInfoRec;
|
|
|
936a6c |
|
|
|
936a6c |
+/* A list of threads currently invoking error handlers on this display
|
|
|
936a6c |
+ * LockDisplay operates differently for these threads, avoiding
|
|
|
936a6c |
+ * generating any requests or reading any events as that can cause
|
|
|
936a6c |
+ * recursion into the error handling code, which will deadlock the
|
|
|
936a6c |
+ * thread.
|
|
|
936a6c |
+ */
|
|
|
936a6c |
+struct _XErrorThreadInfo
|
|
|
936a6c |
+{
|
|
|
936a6c |
+ struct _XErrorThreadInfo *next;
|
|
|
936a6c |
+ xthread_t error_thread;
|
|
|
936a6c |
+};
|
|
|
936a6c |
+
|
|
|
936a6c |
/* XOpenDis.c */
|
|
|
936a6c |
extern int (*_XInitDisplayLock_fn)(Display *dpy);
|
|
|
936a6c |
extern void (*_XFreeDisplayLock_fn)(Display *dpy);
|
|
|
936a6c |
--
|
|
|
936a6c |
2.43.0
|
|
|
936a6c |
|