Fix a race-condition in poll_for_response (#1758384)

This commit is contained in:
Peter Hutterer 2020-11-09 12:37:54 +10:00
parent f431cc3871
commit 6ab8593d2f
2 changed files with 116 additions and 1 deletions

109
libX11-race-condition.patch Normal file
View File

@ -0,0 +1,109 @@
diff --git a/src/Xxcbint.h b/src/Xxcbint.h
index 4ef13d2f..d8293259 100644
--- a/src/Xxcbint.h
+++ b/src/Xxcbint.h
@@ -27,6 +27,7 @@ typedef struct _X11XCBPrivate {
PendingRequest *pending_requests;
PendingRequest *pending_requests_tail;
xcb_generic_event_t *next_event;
+ void *next_response;
char *real_bufmax;
char *reply_data;
int reply_length;
diff --git a/src/xcb_io.c b/src/xcb_io.c
index 0bd1fdf9..4be75408 100644
--- a/src/xcb_io.c
+++ b/src/xcb_io.c
@@ -282,22 +282,83 @@ static xcb_generic_reply_t *poll_for_event(Display *dpy, Bool queued_only)
static xcb_generic_reply_t *poll_for_response(Display *dpy)
{
void *response;
- xcb_generic_error_t *error;
+ xcb_generic_reply_t *event;
PendingRequest *req;
- while(!(response = poll_for_event(dpy, False)) &&
- (req = dpy->xcb->pending_requests) &&
- !req->reply_waiter)
+
+ while(1)
{
+ xcb_generic_error_t *error = NULL;
uint64_t request;
+ Bool poll_queued_only = dpy->xcb->next_response != NULL;
- if(!xcb_poll_for_reply64(dpy->xcb->connection, req->sequence,
- &response, &error)) {
- /* xcb_poll_for_reply64 may have read events even if
- * there is no reply. */
- response = poll_for_event(dpy, True);
+ /* Step 1: is there an event in our queue before the next
+ * reply/error? Return that first.
+ *
+ * If we don't have a reply/error saved from an earlier
+ * invocation we check incoming events too, otherwise only
+ * the ones already queued.
+ */
+ response = poll_for_event(dpy, poll_queued_only);
+ if(response)
break;
+
+ /* Step 2:
+ * Response is NULL, i.e. we have no events.
+ * If we are not waiting for a reply or some other thread
+ * had dibs on the next reply, exit.
+ */
+ req = dpy->xcb->pending_requests;
+ if(!req || req->reply_waiter)
+ break;
+
+ /* Step 3:
+ * We have some response (error or reply) related to req
+ * saved from an earlier invocation of this function. Let's
+ * use that one.
+ */
+ if(dpy->xcb->next_response)
+ {
+ if (((xcb_generic_reply_t*)dpy->xcb->next_response)->response_type == X_Error)
+ {
+ error = dpy->xcb->next_response;
+ response = NULL;
+ }
+ else
+ {
+ response = dpy->xcb->next_response;
+ error = NULL;
+ }
+ dpy->xcb->next_response = NULL;
+ }
+ else
+ {
+ /* Step 4: pull down the next response from the wire. This
+ * should be the 99% case.
+ * xcb_poll_for_reply64() may also pull down events that
+ * happened before the reply.
+ */
+ if(!xcb_poll_for_reply64(dpy->xcb->connection, req->sequence,
+ &response, &error)) {
+ /* if there is no reply/error, xcb_poll_for_reply64
+ * may have read events. Return that. */
+ response = poll_for_event(dpy, True);
+ break;
+ }
+
+ /* Step 5: we have a new response, but we may also have some
+ * events that happened before that response. Return those
+ * first and save our reply/error for the next invocation.
+ */
+ event = poll_for_event(dpy, True);
+ if(event)
+ {
+ dpy->xcb->next_response = error ? error : response;
+ response = event;
+ break;
+ }
}
+ /* Step 6: actually handle the reply/error now... */
request = X_DPY_GET_REQUEST(dpy);
if(XLIB_SEQUENCE_COMPARE(req->sequence, >, request))
{

View File

@ -5,7 +5,7 @@
Summary: Core X11 protocol client library
Name: libX11
Version: 1.6.12
Release: 2%{?gitdate:.%{gitdate}git%{gitversion}}%{?dist}
Release: 3%{?gitdate:.%{gitdate}git%{gitversion}}%{?dist}
License: MIT
URL: http://www.x.org
@ -18,6 +18,8 @@ Source0: https://xorg.freedesktop.org/archive/individual/lib/%{name}-%{version}.
%endif
Patch2: dont-forward-keycode-0.patch
# diff from https://gitlab.freedesktop.org/xorg/lib/libx11/-/merge_requests/53
Patch3: libX11-race-condition.patch
BuildRequires: make
BuildRequires: xorg-x11-util-macros >= 1.11
@ -57,6 +59,7 @@ libX11/libxcb interoperability library
%prep
%setup -q -n %{tarball}-%{?gitdate:%{gitdate}}%{!?gitdate:%{version}}
%patch2 -p1 -b .dont-forward-keycode-0
%patch3 -p1 -b .race-condition
%build
autoreconf -v --install --force
@ -122,6 +125,9 @@ make %{?_smp_mflags} check
%{_mandir}/man5/*.5*
%changelog
* Mon Nov 09 2020 Peter Hutterer <peter.hutterer@redhat.com> 1.6.12-3
- Fix a race-condition in poll_for_response (#1758384)
* Thu Nov 5 11:12:56 AEST 2020 Peter Hutterer <peter.hutterer@redhat.com> - 1.6.12-2
- Add BuildRequires for make