Bug 31657 - MessageQueue::removeIf() has a bug that leads to failed assertions
Summary: MessageQueue::removeIf() has a bug that leads to failed assertions
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Dumitru Daniliuc
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-11-18 18:43 PST by Dumitru Daniliuc
Modified: 2011-07-27 09:18 PDT (History)
3 users (show)

See Also:


Attachments
patch (1.62 KB, patch)
2009-11-18 18:52 PST, Dumitru Daniliuc
dimich: review+
dumi: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Dumitru Daniliuc 2009-11-18 18:43:07 PST
MessageQueue::removeIf() has a bug:

DequeConstIterator<DataType*> found = m_queue.end();   // Adds 'found' to the list of iterators of 'm_queue'
while ((found = m_queue.findIf(predicate) != m_queue.end()) {   // operator=() checks that found.m_queue is not NULL
    DataType* message = *found;
    m_queue.remove(found);    // triggers m_queue.invalidateIteratorators() which sets it.m_queue = NULL for all current iterators of 'm_queue', including 'found'
    delete message;
}

So the first time the loop is run, everything is fine. But then the element to which 'found' points is removed from the list. This triggers Deque::invalidateIterators(), which sets found.m_queue = NULL. The second time 'found = m_queue.findIf(predicate)' is executed, DequeConstIterator::operator=() is called, which eventually calls DequeIteratorBase::operator=(), which ASSERTs that 'found.m_queue' is not NULL. This ASSERT fails. So any time this method is called with a predicate that matches at least one element in the queue, we get an assertion failure.

Solution: Do not carry over the same DequeConstIterator variable from one run of the loop to the other, i.e. declare 'found' inside the loop.
Comment 1 Dumitru Daniliuc 2009-11-18 18:52:31 PST
Created attachment 43477 [details]
patch
Comment 2 Dmitry Titov 2009-11-18 21:23:06 PST
Comment on attachment 43477 [details]
patch

Cool catch! r=me

Please consider also this shape of the loop, it seems a bit simpler (if you agree, it can be changed on landing):

while (true) {
    DequeConstIterator<DataType*> found = m_queue.findIf(predicate);
    if (found == m_queue.end())
        break;

    DataType* message = *found;
    m_queue.remove(found);
    delete message;
}
Comment 3 Dumitru Daniliuc 2009-11-19 11:51:35 PST
(In reply to comment #2)
> (From update of attachment 43477 [details])
> Cool catch! r=me
> 
> Please consider also this shape of the loop, it seems a bit simpler (if you
> agree, it can be changed on landing):
> 
> while (true) {
>     DequeConstIterator<DataType*> found = m_queue.findIf(predicate);
>     if (found == m_queue.end())
>         break;
> 
>     DataType* message = *found;
>     m_queue.remove(found);
>     delete message;
> }

Changing the loop as you suggested. Wasn't sure if 'while (true)' was acceptable in WebKit code.
Comment 4 Dumitru Daniliuc 2009-11-19 12:00:24 PST
Landed as r51198.
Comment 5 Balazs Kelemen 2011-07-27 09:18:50 PDT
I have found a similar bug: https://bugs.webkit.org/show_bug.cgi?id=65263