Bug 50372 - BASE SIGABRT: dereference a past-the-end iterator
Summary: BASE SIGABRT: dereference a past-the-end iterator
Status: RESOLVED FIXED
Alias: None
Product: LibreOffice
Classification: Unclassified
Component: Base (show other bugs)
Version:
(earliest affected)
Master old -3.6
Hardware: Other Linux (All)
: medium normal
Assignee: Lionel Elie Mamane
URL:
Whiteboard: target:3.6.0 target:3.5.5
Keywords:
Depends on:
Blocks:
 
Reported: 2012-05-26 09:02 UTC by Terrence Enger
Modified: 2013-11-15 23:20 UTC (History)
2 users (show)

See Also:
Crash report or crash signature:


Attachments
error message and backtrace (12.94 KB, text/plain)
2012-05-26 09:02 UTC, Terrence Enger
Details
test patch (495 bytes, patch)
2012-05-28 06:04 UTC, Julien Nabet
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Terrence Enger 2012-05-26 09:02:10 UTC
Created attachment 62126 [details]
error message and backtrace

At the stage described in bug 47520 comment 3
<https://bugs.freedesktop.org/show_bug.cgi?id=47520#c3> step 7, the
program issues message "attempt to dereference a past-the-end
iterator" and aborts.  The attached typescript shows the failure and
gdb backtrace.


I have built from master commit d015384, pulled 2012-05-20, configured
as ...
    --disable-mozilla
    --enable-symbols
    --enable-dbgutil
    --enable-crashdump
    --disable-build-mozilla
    --without-system-postgresql
    --enable-debug
and running on ubuntu-natty (11.04).


Working backward in the code and gdb, I observe ...

(*) The dereference happens in OKeySet::refreshRow(), KeySet.cxx line
    1338.  At this point, m_aKeyIter equals m_aKeyMap.end().  The
    error message is true.

(*) m_aKeyIter became equal to m_aKeyMap.end() when
    OKeySet::refreshRow(), KeySet.cxx line 1348 increamented the
    iterator before the recursive call to OKeySet::refreshRow().
    After line 1349 erases the previously addressed element of
    m_aKeyMap, m_aKeyMap.size() is 2.


A couple of simple-minded hacks to the code ( early return from
recursive call to OKeySet::refreshRow(); decrementing instead of
incrementing the pointer before the recursive call ) merely defer the
crash to later in execution.  Guidance welcome.
Comment 1 Julien Nabet 2012-05-28 06:04:06 UTC
Created attachment 62166 [details]
test patch

I didn't try to reproduce on pc Debian x86-64 (master sources updated today). But could you try the patch attached ?

functionnally / technically: if you're at the last row (hope I well interpreted the code), you  - don't need to / can't - refresh the next row since there's no next row
Comment 2 Terrence Enger 2012-05-28 07:14:43 UTC
I have tried a couple of hacks which only deferred the failure to
later points in execution.

(*) The first of these hacks was equivalent to the patch you propose:
    an early return from OKeySet::refreshRow if m_aKeyIter equals
    m_aKeyMap.end().  This makes OKeySet::getBookmark() fail with
    message "getBookmark is possible only when we stand on a valid
    row!".

    Note that the test isAfterLast() early in OKeySet::refreshRow() is
    not a sufficient guard against past-the-end iterator because
    OKeySet::isAfterLast() looks first at m_bRowCountFinal.  So,
    beyond the present problem, I wonder whether there is some reason
    to expect that OKeySet::refreshRow() is never called with false
    m_bCountFinal and m_aKeyIter equal m_aKeyMap.end(); I think this
    combination would make the routine fail every time.

(*) The second was to replace the increment of m_aKeyIter with a
    decrement before KeySet::refreshRow() does the recursive call.
    (Yes, the table has two rows.  And I *think* the first element of
    m_aKeyMap is a dummy anyway, existing just to allow a
    before-the-first test like the after-the-last test which STL
    provides.  So, I guessed that at worst this would result in an
    return from the recursive call because isBeforeFirst().)  I think
    this led to a failure within OSL_ENSURE within OKeySet::getInt(),
    KeySet.cxx line 1441.  (Just "I think" because I made a mess of my
    notes and silly me was not collecting a typescript.)


As the program seems to have fetched all two rows of the table, I
suspect that m_aRowCountFinal should be true--maybe, perhaps.  But,
all I know about that variable is its name and the fact that it seems
to be used a lot.  And the result of my first hack suggests that that
might not be sufficient to fix the problem anyway.


From my comment on bug 47520, it looks like LO as of 2012-04-01 did
not crash opening this table.  As I am running a 32-bit system, I
cannot use bibisect.  Besides, bibsect is only updated to late April,
IIRC.
Comment 3 Julien Nabet 2012-05-28 07:40:13 UTC
You're right Terrence, I reproduced the initial bug and as you said, reproduced the fail with the patch I had attached :-(
A Base dev guru seems to be needed here (hum, let's see...Lionel ? :-))
Comment 4 Terrence Enger 2012-05-31 07:52:32 UTC
Another experimental hack: I added guard ( m_aKeyIter !=
m_aKeyMap.end() ) to the originally failing ...

    m_aKeyIter->second.second.second.is()

This was followed by a past-the-end iterator in
dbaccess::OKeySet::doTryRefetch_throw(), KeySet.cxx line 1295, which
reads ...

    OUpdatedParameter::iterator aUpdateFind = m_aUpdatedParameter.find(m_aKeyIter->first);

The backtrace shows ...

    #5  0x062e413e in dbaccess::OKeySet::doTryRefetch_throw (this=0x9052388) at /home/terry/lo_hacking/git/libo/dbaccess/source/core/api/KeySet.cxx:1295
    #6  0x062e4b77 in dbaccess::OKeySet::refreshRow (this=0x9052388) at /home/terry/lo_hacking/git/libo/dbaccess/source/core/api/KeySet.cxx:1344
    #7  0x062e4c12 in dbaccess::OKeySet::refreshRow (this=0x9052388) at /home/terry/lo_hacking/git/libo/dbaccess/source/core/api/KeySet.cxx:1351
    #8  0x062e2e0b in dbaccess::OKeySet::next (this=0x9052388) at /home/terry/lo_hacking/git/libo/dbaccess/source/core/api/KeySet.cxx:1115
    #9  0x063641d5 in dbaccess::ORowSetCache::fillMatrix (this=0x9052018, _nNewStartPos=@0xbfffcb64, _nNewEndPos=@0xbfffcbb4) at /home/terry/lo_hacking/git/libo/dbaccess/source/core/api/RowSetCache.cxx:827


The symptoms are different from bug 48345, so I am not marking this a
duplicate; but the two bugs do seem to have code in common.  I shall
add a comment to that effect in bug 48345.
Comment 5 Lionel Elie Mamane 2012-05-31 10:02:45 UTC
My guess is that maybe some previous code moved the current record without updating m_aKeyIter. Will try to take a look. I suspect it is related to the fix of bug 48345
Comment 6 Terrence Enger 2012-05-31 12:09:52 UTC
What is "the current row" other than whatever m_aKeyIter points to?

If it could mean an element of m_aKeyMap, perchance ... well, the
previously current element fell victim to `m_aKeyMap.erase(aTemp);` at
KeySet.xxx line 1349.
Comment 7 Lionel Elie Mamane 2012-06-01 02:45:31 UTC
Fixed in my development tree. Will push later today.
Comment 8 Not Assigned 2012-06-01 08:02:45 UTC
Lionel Elie Mamane committed a patch related to this issue.
It has been pushed to "master":

http://cgit.freedesktop.org/libreoffice/core/commit/?id=76a74b72db45a5aa3f064034e0fa05440aefb52b

fdo#50372: crash when refresh of last already-known row unexpectedly fails
Comment 9 Not Assigned 2012-06-05 04:29:51 UTC
Lionel Elie Mamane committed a patch related to this issue.
It has been pushed to "libreoffice-3-5":

http://cgit.freedesktop.org/libreoffice/core/commit/?id=38786a76cf3dbc421635d40354e8c9ece409c6c5&g=libreoffice-3-5

fdo#50372: crash when refresh of last already-known row unexpectedly fails


It will be available in LibreOffice 3.5.5.
Comment 10 Terrence Enger 2012-06-07 06:34:49 UTC
The past-the-end iterator seems to be fixed.  Thank you, Lionel, for
this.

Of course, fdo#50575 remains.