Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault when readline history is more then 2 * history size #74040

Closed
nirs mannequin opened this issue Mar 19, 2017 · 24 comments
Closed

Segfault when readline history is more then 2 * history size #74040

nirs mannequin opened this issue Mar 19, 2017 · 24 comments
Labels
3.7 (EOL) end of life extension-modules C modules in the Modules dir type-crash A hard crash of the interpreter, possibly with a core dump

Comments

@nirs
Copy link
Mannequin

nirs mannequin commented Mar 19, 2017

BPO 29854
Nosy @Yhg1s, @nirs, @vstinner, @berkerpeksag, @vadmium, @serhiy-storchaka
PRs
  • bpo-29854: Fix segfault in call_readline() #728
  • bpo-29854: Print readline version information when running test suite #2618
  • bpo-29854: test_readline logs versions in verbose mode #2619
  • bpo-29854: Skip history-size test on older readline #2621
  • [3.6] bpo-29854 Backport for 3.6 #2633
  • [3.5] bpo-29854: Backport to 3.5 #2636
  • [2.7] bpo-29854: Fix segfault in call_readline() #2637
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2017-07-10.21:09:39.064>
    created_at = <Date 2017-03-19.22:15:24.074>
    labels = ['extension-modules', '3.7', 'type-crash']
    title = 'Segfault when readline history is more then 2 * history size'
    updated_at = <Date 2017-10-18.13:54:07.568>
    user = 'https://github.com/nirs'

    bugs.python.org fields:

    activity = <Date 2017-10-18.13:54:07.568>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2017-07-10.21:09:39.064>
    closer = 'berker.peksag'
    components = ['Extension Modules']
    creation = <Date 2017-03-19.22:15:24.074>
    creator = 'nirs'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 29854
    keywords = []
    message_count = 24.0
    messages = ['289865', '289881', '289883', '290044', '293981', '293988', '294101', '297863', '297876', '297877', '297879', '297880', '297881', '297884', '297887', '297895', '297896', '297897', '297955', '297967', '297989', '298082', '298084', '304578']
    nosy_count = 7.0
    nosy_names = ['twouters', 'nirs', 'vstinner', 'berker.peksag', 'martin.panter', 'serhiy.storchaka', 'Nir Soffer']
    pr_nums = ['728', '2618', '2619', '2621', '2633', '2636', '2637']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'crash'
    url = 'https://bugs.python.org/issue29854'
    versions = ['Python 2.7', 'Python 3.5', 'Python 3.6', 'Python 3.7']

    @nirs
    Copy link
    Mannequin Author

    nirs mannequin commented Mar 19, 2017

    GNU readline let the user select limit the history size by setting:

    $ cat ~/.inputrc 
    set history-size 1000

    So I cooked this test script:

    $ cat history.py 
    from __future__ import print_function
    import readline
    readline.read_history_file(".history")
    
    print("current_history_length", readline.get_current_history_length())
    print("history_length", readline.get_history_length())
    print("history_get_item(1)", readline.get_history_item(1))
    print("history_get_item(1000)", readline.get_history_item(1000))
    
    input()
    
    readline.write_history_file(".history")

    And this history file generator:

    $ cat make-history 
    for i in range(2000):
        print("%04d" % i)

    Generating .history file with 2000 entries:
    $ python3 make-history > .history

    Finally running the test script:

    $ python3 history.py 
    current_history_length 1000
    history_length -1
    history_get_item(1) None
    history_get_item(1000) None
    please crash
    Segmentation fault (core dumped)

    So we have few issues here:

    • segfault
    • history_get_item returns None for both 1 and 1000
      although we have 1000 items in history
    • history_length is always wrong (-1), instead of
      the expected value (1000), set in .inputrc

    Running with gdb we see:

    $ gdb python3
    GNU gdb (GDB) Fedora 7.12.1-46.fc25
    Copyright (C) 2017 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "x86_64-redhat-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from python3...Reading symbols from /usr/lib/debug/usr/libexec/system-python.debug...done.
    done.

    (gdb) run history.py
    Starting program: /usr/bin/python3 history.py
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib64/libthread_db.so.1".
    current_history_length 1000
    history_length -1
    history_get_item(1) None
    history_get_item(1000) None
    crash?

    Program received signal SIGSEGV, Segmentation fault.
    0x00007fffeff60fab in call_readline (sys_stdin=<optimized out>, sys_stdout=<optimized out>, prompt=<optimized out>) at /usr/src/debug/Python-3.5.2/Modules/readline.c:1281
    1281 line = (const char *)history_get(length)->line;

    (gdb) list
    1276 if (using_libedit_emulation) {
    1277 /* handle older 0-based or newer 1-based indexing */
    1278 line = (const char *)history_get(length + libedit_history_start - 1)->line;
    1279 } else
    1280 #endif /* __APPLE__ */
    1281 line = (const char *)history_get(length)->line;
    1282 else
    1283 line = "";
    1284 if (strcmp(p, line))
    1285 add_history(p);

    So we assume that history_get(length) returns non-null
    when length > 0, but this assumption is not correct.

    In 2 other usages in Modules/readline.c, we validate
    that history_get() return value is not null before
    using it.

    If we change the .history contents to 1999 lines, we get:

    $ python3 make-history | head -1999 > .history
    
    $ python3 history.py
    current_history_length 1000
    history_length -1
    history_get_item(1) None
    history_get_item(1000) 0999
    crash?
    
    $ wc -l .history
    1000 .history
    
    $ head -1 .history
    1000
    $ tail -1 .history
    crash?

    So now it does not crash, but item 1 is still None.

    Trying again with history file with 1000 entries:

    $ python3 make-history | head -1000 > .history
    
    $ python3 history.py
    current_history_length 1000
    history_length -1
    history_get_item(1) 0000
    history_get_item(1000) 0999
    looks fine!
    
    $ wc -l .history
    1000 .history
    
    $ head -1 history
    head: cannot open 'history' for reading: No such file or directory
    
    $ head -1 .history
    0001
    
    $ tail -1 .history
    looks fine!

    Finally trying with 1001 items:

    $ python3 make-history | head -1001 > .history
    
    $ python3 history.py
    current_history_length 1000
    history_length -1
    history_get_item(1) None
    history_get_item(1000) 0999

    And item 1 is wrong.

    I got same results with python 2.7, 3.5 and master
    on fedora 25.

    The root cause seems to be a readline bug when history
    file is bigger than the history-size in .inputrc,
    but I could not find yet readline library documentation,
    so I don't know if the issues is incorrect usage of the
    readline apis, or bug in readline.

    @nirs nirs mannequin added 3.7 (EOL) end of life extension-modules C modules in the Modules dir labels Mar 19, 2017
    @serhiy-storchaka
    Copy link
    Member

    The fix LGTM. Any chance to write a test? And please add an entry in Misc/NEWS.

    @serhiy-storchaka serhiy-storchaka added the type-crash A hard crash of the interpreter, possibly with a core dump label Mar 20, 2017
    @nirs
    Copy link
    Mannequin Author

    nirs mannequin commented Mar 20, 2017

    Sure, I'll add news entry and tests.

    @vadmium
    Copy link
    Member

    vadmium commented Mar 23, 2017

    Gnu Readline comes includes its own documentation (e.g. /usr/share/info/history.info.gz on my computer). It is also at <https://cnswww.cns.cwru.edu/php/chet/readline/history.html\>.

    Perhaps the history_base value is relevant; see some of the comments starting at <https://bugs.python.org/issue6953#msg100466\>.

    It would be interesting to see if Apple Editline is affected. According to the comment in the get_history_item function, history_get might crash before returning the null pointer. Is there some other workaround that avoids calling history_get?

    @nirs
    Copy link
    Mannequin Author

    nirs mannequin commented May 19, 2017

    I think the issue can be solved in readline or in the code using it, but I don't have more time to dig into this, and I think that python should not crash in this case.

    I don't have an environment to test Apple editline, so I cannot test this issue. The PR includes a test case now, running the new test on OS X will tell us if this is the case.

    @vadmium
    Copy link
    Member

    vadmium commented May 20, 2017

    I suspect the test won’t be effective with Editline (either fail, or silently pass without testing the bug). From memory Editline uses an ~/.editrc file, not INPUTRC, with different syntax and configuration.

    @nirs
    Copy link
    Mannequin Author

    nirs mannequin commented May 21, 2017

    This issue does not exist on OS X 10.11.6 (latest my old mac can install).

    I tested using .editrc file:

    $ cat ~/.editrc
    history size 5

    With history file with 10 items that crashes on Linux using GNU readline.

    This settings is ignored, adding items to the history file without truncating it to 5 items.

    I tested also truncating the size using readline.set_history_size(). It works correctly, but this means every application need to implement its own readline configuration, instead of reusing the system readline configuration.

    So this bug is relevant only to GNU readline, need to skip this test when using libedit.

    @berkerpeksag
    Copy link
    Member

    New changeset fae8f4a by Berker Peksag (Nir Soffer) in branch 'master':
    bpo-29854: Fix segfault in call_readline() (GH-728)
    fae8f4a

    @vstinner
    Copy link
    Member

    vstinner commented Jul 7, 2017

    The test fails on AMD64 FreeBSD 10.x Shared 3.x:

    http://buildbot.python.org/all/builders/AMD64%20FreeBSD%2010.x%20Shared%203.x/builds/551/steps/test/logs/stdio

    ======================================================================
    FAIL: test_history_size (test.test_readline.TestReadline)
    ----------------------------------------------------------------------

    Traceback (most recent call last):
      File "/usr/home/buildbot/python/3.x.koobs-freebsd10/build/Lib/test/test_readline.py", line 247, in test_history_size
        self.assertEqual(len(lines), history_size)
    AssertionError: 21 != 10

    @vstinner
    Copy link
    Member

    vstinner commented Jul 7, 2017

    Similar failure on x86 Tiger 3.x.

    Maybe we need to skip the test on old macOS and old FreeBSD versions? Maybe it's related to the libncurses version?

    http://buildbot.python.org/all/builders/x86%20Tiger%203.x/builds/908/steps/test/logs/stdio

    ======================================================================
    FAIL: test_history_size (test.test_readline.TestReadline)
    ----------------------------------------------------------------------

    Traceback (most recent call last):
      File "/Users/db3l/buildarea/3.x.bolen-tiger/build/Lib/test/test_readline.py", line 247, in test_history_size
        self.assertEqual(len(lines), history_size)
    AssertionError: 21 != 10

    @berkerpeksag
    Copy link
    Member

    Similar failure on x86 Tiger 3.x.

    This one is interesting. I thought we don't have OS X buildbots with readline installed.

    I would prefer skipping the test based on readline version installed.

    Side note: I think we should print readline, sqlite3 etc. versions in

    def display_header(self):

    @berkerpeksag
    Copy link
    Member

    I've opened PR 2618 to print readline version and implementation in regrtest's display_header() method.

    @NirSoffer
    Copy link
    Mannequin

    NirSoffer mannequin commented Jul 7, 2017

    The failures looks like libedit failures on OS X, where history size is ignored. The test is skipped if is_editline is set, we should probably skip on these platforms too.

    @vstinner
    Copy link
    Member

    vstinner commented Jul 7, 2017

    New changeset 1881bef by Victor Stinner in branch 'master':
    bpo-29854: test_readline logs versions (bpo-2619)
    1881bef

    @vstinner
    Copy link
    Member

    vstinner commented Jul 7, 2017

    AMD64 FreeBSD 9.x 3.x, test_history_size() fails:

    readline version: 0x502
    readline runtime version: 0x502
    readline library version: '5.2'
    use libedit emulation? False

    AMD64 FreeBSD 10.x Shared 3.x, test_history_size() fails:

    readline version: 0x502
    readline runtime version: 0x502
    readline library version: '5.2'
    use libedit emulation? False

    x86 Tiger 3.x, test_history_size() fails:

    readline version: 0x501
    readline runtime version: 0x501
    readline library version: '5.1'
    use libedit emulation? False

    --

    My Linux box, test_history_size() pass:

    readline version: 0x603
    readline runtime version: 0x603
    readline library version: '6.3'
    use libedit emulation? False

    @NirSoffer
    Copy link
    Mannequin

    NirSoffer mannequin commented Jul 7, 2017

    So we have version 0x502 without libedit emulation succeeding on
    FreeBSD 9.x, and failing on 10.x.

    I think we are missing something, or maybe the libedit check is wrong.

    We need results from all builders to do something with this. I think
    at least for now we want to see readline info from all builders, not only
    for failed tests.

    Maybe switch the failing test to run only on Linux for now?

    @berkerpeksag
    Copy link
    Member

    So we have version 0x502 without libedit emulation succeeding on
    FreeBSD 9.x, and failing on 10.x.

    test_history_size() fails on FreeBSD 9.x too:

    ======================================================================
    FAIL: test_history_size (test.test_readline.TestReadline)
    ----------------------------------------------------------------------

    Traceback (most recent call last):
      File "/usr/home/buildbot/python/3.x.koobs-freebsd9/build/Lib/test/test_readline.py", line 263, in test_history_size
        self.assertEqual(len(lines), history_size)
    AssertionError: 21 != 10

    http://buildbot.python.org/all/builders/AMD64%20FreeBSD%209.x%203.x/builds/316/steps/test/logs/stdio

    @berkerpeksag
    Copy link
    Member

    According to https://cnswww.cns.cwru.edu/php/chet/readline/CHANGES, the history-size setting was added in readline 6.0:

    e.  A new user-settable variable, `history-size', allows setting the maximum
        number of entries in the history list.
    

    The only thing we need to do is skip the test if readline version is older than 6.0.

    We discussed this with Nir on IRC, and he will send another PR to tweak the test.

    @berkerpeksag
    Copy link
    Member

    New changeset aa6a4d6 by Berker Peksag (Nir Soffer) in branch 'master':
    bpo-29854: Skip history-size test on older readline (GH-2621)
    aa6a4d6

    @berkerpeksag
    Copy link
    Member

    New changeset 04f77d4 by Berker Peksag (Nir Soffer) in branch '3.6':
    [3.6] bpo-29854: Fix segfault in call_readline() (GH-728)
    04f77d4

    @berkerpeksag
    Copy link
    Member

    New changeset 68c3724 by Berker Peksag (Nir Soffer) in branch '3.5':
    [3.5] bpo-29854: Fix segfault in call_readline() (GH-728)
    68c3724

    @berkerpeksag
    Copy link
    Member

    New changeset bfa4fe4 by Berker Peksag (Nir Soffer) in branch '2.7':
    [2.7] bpo-29854: Fix segfault in call_readline() (GH-728)
    bfa4fe4

    @berkerpeksag
    Copy link
    Member

    Ok, I think we can finally close this one :) Thank you, everyone!

    @vstinner
    Copy link
    Member

    FYI I added the test.pythoninfo utility as a follow-up of this issue to log many informations to debug Python, not only the readline version: see bpo-30871.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.7 (EOL) end of life extension-modules C modules in the Modules dir type-crash A hard crash of the interpreter, possibly with a core dump
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants