|
|
f20033 |
From 77f8517710a724fa1f29de8ad806692782f962bd Mon Sep 17 00:00:00 2001
|
|
|
f20033 |
From: Frediano Ziglio <fziglio@redhat.com>
|
|
|
f20033 |
Date: Wed, 29 Jan 2020 09:06:54 +0000
|
|
|
f20033 |
Subject: [PATCH libX11] Fix poll_for_response race condition
|
|
|
f20033 |
|
|
|
f20033 |
In poll_for_response is it possible that event replies are skipped
|
|
|
f20033 |
and a more up to date message reply is returned.
|
|
|
f20033 |
This will cause next poll_for_event call to fail aborting the program.
|
|
|
f20033 |
|
|
|
f20033 |
This was proved using some slow ssh tunnel or using some program
|
|
|
f20033 |
to slow down server replies (I used a combination of xtrace and strace).
|
|
|
f20033 |
|
|
|
f20033 |
How the race happens:
|
|
|
f20033 |
- program enters into poll_for_response;
|
|
|
f20033 |
- poll_for_event is called but the server didn't still send the reply;
|
|
|
f20033 |
- pending_requests is not NULL because we send a request (see call
|
|
|
f20033 |
to append_pending_request in _XSend);
|
|
|
f20033 |
- xcb_poll_for_reply64 is called from poll_for_response;
|
|
|
f20033 |
- xcb_poll_for_reply64 will read from server, at this point
|
|
|
f20033 |
server reply with an event (say sequence N) and the reply to our
|
|
|
f20033 |
last request (say sequence N+1);
|
|
|
f20033 |
- xcb_poll_for_reply64 returns the reply for the request we asked;
|
|
|
f20033 |
- last_request_read is set to N+1 sequence in poll_for_response;
|
|
|
f20033 |
- poll_for_response returns the response to the request;
|
|
|
f20033 |
- poll_for_event is called (for instance from another poll_for_response);
|
|
|
f20033 |
- event with sequence N is retrieved;
|
|
|
f20033 |
- the N sequence is widen, however, as the "new" number computed from
|
|
|
f20033 |
last_request_read is less than N the number is widened to N + 2^32
|
|
|
f20033 |
(assuming last_request_read is still contained in 32 bit);
|
|
|
f20033 |
- poll_for_event enters the nested if statement as req is NULL;
|
|
|
f20033 |
- we compare the widen N (which now does not fit into 32 bit) with
|
|
|
f20033 |
request (which fits into 32 bit) hitting the throw_thread_fail_assert.
|
|
|
f20033 |
|
|
|
f20033 |
I propose to change the widen to not go too far from the wide number
|
|
|
f20033 |
instead of supposing the result is always bigger than the wide number
|
|
|
f20033 |
passed.
|
|
|
f20033 |
|
|
|
f20033 |
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
|
|
f20033 |
---
|
|
|
f20033 |
src/xcb_io.c | 4 +---
|
|
|
f20033 |
1 file changed, 1 insertion(+), 3 deletions(-)
|
|
|
f20033 |
|
|
|
f20033 |
diff --git a/src/xcb_io.c b/src/xcb_io.c
|
|
|
f20033 |
index 6a12d150..2aacbda3 100644
|
|
|
f20033 |
--- a/src/xcb_io.c
|
|
|
f20033 |
+++ b/src/xcb_io.c
|
|
|
f20033 |
@@ -201,12 +201,10 @@ static int handle_error(Display *dpy, xError *err, Bool in_XReply)
|
|
|
f20033 |
}
|
|
|
f20033 |
|
|
|
f20033 |
/* Widen a 32-bit sequence number into a 64bit (uint64_t) sequence number.
|
|
|
f20033 |
- * Treating the comparison as a 1 and shifting it avoids a conditional branch.
|
|
|
f20033 |
*/
|
|
|
f20033 |
static void widen(uint64_t *wide, unsigned int narrow)
|
|
|
f20033 |
{
|
|
|
f20033 |
- uint64_t new = (*wide & ~((uint64_t)0xFFFFFFFFUL)) | narrow;
|
|
|
f20033 |
- *wide = new + (((uint64_t)(new < *wide)) << 32);
|
|
|
f20033 |
+ *wide += (int32_t) (narrow - *wide);
|
|
|
f20033 |
}
|
|
|
f20033 |
|
|
|
f20033 |
/* Thread-safety rules:
|
|
|
f20033 |
--
|
|
|
f20033 |
2.23.0
|
|
|
f20033 |
|