xquartz dereferencing a NULL pointer (patch 2)
Jeremy Huddleston
jeremyhu at freedesktop.org
Wed Nov 5 09:35:04 PST 2008
On Nov 5, 2008, at 07:34, George Peter Staplin wrote:
>
> On Nov 5, 2008, at 12:33 AM, Jeremy Huddleston wrote:
>
>> The locks inside mieqProcessInputEvents() should do nothing.
>> mieqProcessInputEvents is the only thread that modifies
>> miEventQueue.head, and we don't care if miEventQueue.tail changes
>> out from under us so long as miEventQueue.tail is changed AFTER the
>> data has been pushed to it (which might actually be the problem).
>>
>
> Your patch seems to fix the problem too. Are we relying on certain
> operations being atomic?
That's a good point. Let's take a look at this in a bit more detail,
so hopefully I can convince you that we don't actually care about the
atomicity of this opperation.
mieqProcessInputEvents does a check to see if head != tail in order to
determine if there are more events to process. If "tail = newtail"
and "head != tail" are atomic, this should work fine. I argue that we
don't care about "head != tail" itself being atomic, but we do care
that the value of tail copied into register be valid. We don't care
if it's "old" or "new". If it's "old", then the last event won't be
processed until the next time mieqProcessInputEvents is called (not
really a problem). If it's "new" then we know the event is fully in
the queue and ready to be processed. So miEventQueue.tail needs to be
valid. Without optimization "miEventQueue.tail = newtail" should be a
single store-word or copy-word type instruction. With optimization,
that instruction would possibly be ignored (for the case when
(isMotion && isMotion == miEventQueue.lastMotion && oldtail !=
miEventQueue.head) [see below as this also has a different problem I
just realized]) or would be atomic. I guess we run the risk that it
is optimized as:
newtail = (oldtail + 1) % QUEUE_SIZE;
miEventQueue.tail = newtail;
becoming
miEventQueue.tail++;
miEventQueue.tail |= QUEUE_SIZE - 1;
with the intermediate value actually stored in memory for some
reason... in that case, the != would still succeed and
mieqProcessInputEvents would behave correctly EXCEPT if the new
miEventQueue.tail was now QUEUE_SIZE in which case it would process
events forever until the other thread got context back to finish the
mod/store...
You're starting to convince me a little that we need a lock with this
argument, but it really relies on a compiler being utterly stupid...
this next case, however, is more persuasive...
So for the case when (isMotion && isMotion == miEventQueue.lastMotion
&& oldtail != miEventQueue.head), we essentially modify the last item
already in the queue... but this is an event that the server thread
could already be processing (I think this code might be what caused
that "must be reentrant with ProcessInputEvents" comment that I
dismissed earlier). This is essentially an optimization (combining a
delta-x1 and delta-x2 mouse motion into a single delta-(x1 + x2) mouse
motion). At first glance, we could do something like this:
diff --git a/mi/mieq.c b/mi/mieq.c
index 062dede..ac2eedb 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -167,6 +168,8 @@ mieqEnqueue(DeviceIntPtr pDev, xEvent *e)
if (isMotion && isMotion == miEventQueue.lastMotion &&
oldtail != miEventQueue.head) {
oldtail = (oldtail - 1) % QUEUE_SIZE;
+
+ miEventQueue.tail = oldtail;
}
else {
static int stuck = 0;
but that only prevents the mieqProcessInputEvents thread from
processing the event if it hasn't already done the check. Since we
don't have a mutex, we need to assume a context switch could happen
while the server thread is already in the middle of processing this
event... I see two possibilities for handling this:
1) Lock inside mieqProcessInputEvents
2) Disable this optimization when we have multiple threads playing
with mieqEventQueue and just push a second MotionNotify event.
Thoughts?
>
>
> --George
>
>> Try this:
>>
>> diff --git a/mi/mieq.c b/mi/mieq.c
>> index e93d24f..7902608 100644
>> --- a/mi/mieq.c
>> +++ b/mi/mieq.c
>> @@ -113,7 +113,8 @@ mieqEnqueue(DeviceIntPtr pDev, xEvent *e)
>> #ifdef XQUARTZ
>> pthread_mutex_lock(&miEventQueueMutex);
>> #endif
>> - unsigned int oldtail = miEventQueue.tail, newtail;
>> + unsigned int oldtail = miEventQueue.tail
>> + unsigned int newtail = oldtail;
>> int isMotion = 0;
>> deviceValuator *v = (deviceValuator *) e;
>> EventPtr laste = &miEventQueue.events[(oldtail -
>> 1) %
>> @@ -173,7 +174,6 @@ mieqEnqueue(DeviceIntPtr pDev, xEvent *e)
>> #endif
>> return;
>> }
>> - miEventQueue.tail = newtail;
>> }
>>
>> memcpy(&(miEventQueue.events[oldtail].event[0]), e,
>> sizeof(xEvent));
>> @@ -192,6 +192,7 @@ mieqEnqueue(DeviceIntPtr pDev, xEvent *e)
>> miEventQueue.events[oldtail].pDev = pDev;
>>
>> miEventQueue.lastMotion = isMotion;
>> + miEventQueue.tail = newtail;
>> #ifdef XQUARTZ
>> pthread_mutex_unlock(&miEventQueueMutex);
>> #endif
>>
>>
>> --Jeremy
>>
>>
>> On Nov 4, 2008, at 22:33, George Peter Staplin wrote:
>>
>>>>
>>>
>>> I have yet another patch. I think this is better. The long while
>>> loop made me miss the forest from the trees.
>>>
>>> Now we lock again at the end of the loop, and unlock on exit of
>>> the function. It still works fine in the test cases with this in
>>> place.
>>>
>>> I'm not sure it's 100% perfect. I think recursion could introduce
>>> problems, but it's interesting how it makes the fault go away.
>>> Perhaps we need a recursive mutex?
>>>
>>> <xserver_Nov_4_mieq_fix-2.patch>
>>>
>>>
>>> /* suspected race area*/
>>> while (miEventQueue.head != miEventQueue.tail) {
>>> /* suspected race area */
>>> ...
>>> memcpy(&e, &miEventQueue.events[miEventQueue.head],
>>> sizeof(EventRec));
>>> miEventQueue.head = (miEventQueue.head + 1) % QUEUE_SIZE;
>>>
>>> --George
>>>
>>>
>>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 3040 bytes
Desc: not available
URL: <http://lists.x.org/archives/xorg/attachments/20081105/615dbe85/attachment.bin>
More information about the xorg
mailing list