Kaydet (Commit) 04f77d46 authored tarafından Nir Soffer's avatar Nir Soffer Kaydeden (comit) Berker Peksag

[3.6] bpo-29854: Fix segfault in call_readline() (GH-728)

If history-length is set in .inputrc, and the history file is double the
history size (or more), history_get(N) returns NULL, and python
segfaults. Fix that by checking for NULL return value.

It seems that the root cause is incorrect handling of bigger history in
readline, but Python should not segfault even if readline returns
unexpected value.

This issue affects only GNU readline. When using libedit emulation
system history size option does not work.
üst 03e0df66
...@@ -9,7 +9,7 @@ import subprocess ...@@ -9,7 +9,7 @@ import subprocess
import sys import sys
import tempfile import tempfile
import unittest import unittest
from test.support import import_module, unlink, TESTFN from test.support import import_module, unlink, temp_dir, TESTFN
from test.support.script_helper import assert_python_ok from test.support.script_helper import assert_python_ok
# Skip tests if there is no readline module # Skip tests if there is no readline module
...@@ -210,13 +210,57 @@ print("history", ascii(readline.get_history_item(1))) ...@@ -210,13 +210,57 @@ print("history", ascii(readline.get_history_item(1)))
self.assertIn(b"result " + expected + b"\r\n", output) self.assertIn(b"result " + expected + b"\r\n", output)
self.assertIn(b"history " + expected + b"\r\n", output) self.assertIn(b"history " + expected + b"\r\n", output)
# We have 2 reasons to skip this test:
# - readline: history size was added in 6.0
# See https://cnswww.cns.cwru.edu/php/chet/readline/CHANGES
# - editline: history size is broken on OS X 10.11.6.
# Newer versions were not tested yet.
@unittest.skipIf(readline._READLINE_VERSION < 0x600,
"this readline version does not support history-size")
@unittest.skipIf(is_editline,
"editline history size configuration is broken")
def test_history_size(self):
history_size = 10
with temp_dir() as test_dir:
inputrc = os.path.join(test_dir, "inputrc")
with open(inputrc, "wb") as f:
f.write(b"set history-size %d\n" % history_size)
history_file = os.path.join(test_dir, "history")
with open(history_file, "wb") as f:
# history_size * 2 items crashes readline
data = b"".join(b"item %d\n" % i
for i in range(history_size * 2))
f.write(data)
script = """
import os
import readline
history_file = os.environ["HISTORY_FILE"]
readline.read_history_file(history_file)
input()
readline.write_history_file(history_file)
"""
env = dict(os.environ)
env["INPUTRC"] = inputrc
env["HISTORY_FILE"] = history_file
run_pty(script, input=b"last input\r", env=env)
with open(history_file, "rb") as f:
lines = f.readlines()
self.assertEqual(len(lines), history_size)
self.assertEqual(lines[-1].strip(), b"last input")
def run_pty(script, input=b"dummy input\r"): def run_pty(script, input=b"dummy input\r", env=None):
pty = import_module('pty') pty = import_module('pty')
output = bytearray() output = bytearray()
[master, slave] = pty.openpty() [master, slave] = pty.openpty()
args = (sys.executable, '-c', script) args = (sys.executable, '-c', script)
proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave) proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env)
os.close(slave) os.close(slave)
with ExitStack() as cleanup: with ExitStack() as cleanup:
cleanup.enter_context(proc) cleanup.enter_context(proc)
......
Fix segfault in readline when using readline's history-size option. Patch
by Nir Soffer.
...@@ -1347,15 +1347,17 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) ...@@ -1347,15 +1347,17 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
if (should_auto_add_history && n > 0) { if (should_auto_add_history && n > 0) {
const char *line; const char *line;
int length = _py_get_history_length(); int length = _py_get_history_length();
if (length > 0) if (length > 0) {
HIST_ENTRY *hist_ent;
#ifdef __APPLE__ #ifdef __APPLE__
if (using_libedit_emulation) { if (using_libedit_emulation) {
/* handle older 0-based or newer 1-based indexing */ /* handle older 0-based or newer 1-based indexing */
line = (const char *)history_get(length + libedit_history_start - 1)->line; hist_ent = history_get(length + libedit_history_start - 1);
} else } else
#endif /* __APPLE__ */ #endif /* __APPLE__ */
line = (const char *)history_get(length)->line; hist_ent = history_get(length);
else line = hist_ent ? hist_ent->line : "";
} else
line = ""; line = "";
if (strcmp(p, line)) if (strcmp(p, line))
add_history(p); add_history(p);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment