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.
This commit is contained in:
parent
25a4206c24
commit
fae8f4a9cb
|
@ -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,50 @@ 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)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
def run_pty(script, input=b"dummy input\r"):
|
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", 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)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
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)
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue