Bug report
Bug description:
LeakSanitizer detects memory leaks in the readline module when running test_audit.
Affected Code
Four functions in Modules/readline.c:
- line 298:
PyUnicode_FSConverter call
|
/* Exported function to load a readline history file */ |
|
|
|
/*[clinic input] |
|
@critical_section |
|
readline.read_history_file |
|
|
|
filename as filename_obj: object = None |
|
/ |
|
|
|
Load a readline history file. |
|
|
|
The default filename is ~/.history. |
|
[clinic start generated code]*/ |
|
|
|
static PyObject * |
|
readline_read_history_file_impl(PyObject *module, PyObject *filename_obj) |
|
/*[clinic end generated code: output=66a951836fb54fbb input=5d86fd7813172a67]*/ |
|
{ |
|
PyObject *filename_bytes; |
|
if (filename_obj != Py_None) { |
|
if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) |
|
return NULL; |
|
if (PySys_Audit("open", "OCi", filename_obj, 'r', 0) < 0) { |
|
return NULL; |
|
} |
|
errno = read_history(PyBytes_AS_STRING(filename_bytes)); |
|
Py_DECREF(filename_bytes); |
|
} else { |
|
/* Use the documented default filename here, |
|
* even though readline expands it different internally. */ |
|
if (PySys_Audit("open", "sCi", "~/.history", 'r', 0) < 0) { |
|
return NULL; |
|
} |
|
errno = read_history(NULL); |
|
} |
|
if (errno) |
|
return PyErr_SetFromErrno(PyExc_OSError); |
|
Py_RETURN_NONE; |
|
} |
- line 342:
PyUnicode_FSConverter call
|
/* Exported function to save a readline history file */ |
|
|
|
/*[clinic input] |
|
@critical_section |
|
readline.write_history_file |
|
|
|
filename as filename_obj: object = None |
|
/ |
|
|
|
Save a readline history file. |
|
|
|
The default filename is ~/.history. |
|
[clinic start generated code]*/ |
|
|
|
static PyObject * |
|
readline_write_history_file_impl(PyObject *module, PyObject *filename_obj) |
|
/*[clinic end generated code: output=fbcad13d8ef59ae6 input=34aaada95120cfaa]*/ |
|
{ |
|
PyObject *filename_bytes; |
|
const char *filename; |
|
int err; |
|
if (filename_obj != Py_None) { |
|
if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) |
|
return NULL; |
|
filename = PyBytes_AS_STRING(filename_bytes); |
|
if (PySys_Audit("open", "OCi", filename_obj, 'w', 0) < 0) { |
|
return NULL; |
|
} |
|
} else { |
|
filename_bytes = NULL; |
|
filename = NULL; |
|
/* Use the documented default filename here, |
|
* even though readline expands it different internally. */ |
|
if (PySys_Audit("open", "sCi", "~/.history", 'w', 0) < 0) { |
|
return NULL; |
|
} |
|
} |
|
errno = err = write_history(filename); |
|
int history_length = FT_ATOMIC_LOAD_INT_RELAXED(_history_length); |
|
if (!err && history_length >= 0) |
|
history_truncate_file(filename, history_length); |
|
Py_XDECREF(filename_bytes); |
|
errno = err; |
|
if (errno) |
|
return PyErr_SetFromErrno(PyExc_OSError); |
|
Py_RETURN_NONE; |
|
} |
- line 399:
PyUnicode_FSConverter call
|
/*[clinic input] |
|
@critical_section |
|
readline.append_history_file |
|
|
|
nelements: int |
|
filename as filename_obj: object = None |
|
/ |
|
|
|
Append the last nelements items of the history list to file. |
|
|
|
The default filename is ~/.history. |
|
[clinic start generated code]*/ |
|
|
|
static PyObject * |
|
readline_append_history_file_impl(PyObject *module, int nelements, |
|
PyObject *filename_obj) |
|
/*[clinic end generated code: output=5df06fc9da56e4e4 input=78a6061a8d3a0275]*/ |
|
{ |
|
if (nelements < 0) |
|
{ |
|
PyErr_SetString(PyExc_ValueError, "nelements must be positive"); |
|
return NULL; |
|
} |
|
|
|
PyObject *filename_bytes; |
|
const char *filename; |
|
int err; |
|
if (filename_obj != Py_None) { |
|
if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) |
|
return NULL; |
|
filename = PyBytes_AS_STRING(filename_bytes); |
|
if (PySys_Audit("open", "OCi", filename_obj, 'a', 0) < 0) { |
|
return NULL; |
|
} |
|
} else { |
|
filename_bytes = NULL; |
|
filename = NULL; |
|
/* Use the documented default filename here, |
|
* even though readline expands it different internally. */ |
|
if (PySys_Audit("open", "sCi", "~/.history", 'a', 0) < 0) { |
|
return NULL; |
|
} |
|
} |
|
errno = err = append_history( |
|
nelements - libedit_append_replace_history_offset, filename); |
|
int history_length = FT_ATOMIC_LOAD_INT_RELAXED(_history_length); |
|
if (!err && history_length >= 0) |
|
history_truncate_file(filename, history_length); |
|
Py_XDECREF(filename_bytes); |
|
errno = err; |
|
if (errno) |
|
return PyErr_SetFromErrno(PyExc_OSError); |
|
Py_RETURN_NONE; |
|
} |
- line 255:
PyUnicode_FSConverter call
|
/*[clinic input] |
|
@critical_section |
|
readline.read_init_file |
|
|
|
filename as filename_obj: object = None |
|
/ |
|
|
|
Execute a readline initialization file. |
|
|
|
The default filename is the last filename used. |
|
[clinic start generated code]*/ |
|
|
|
static PyObject * |
|
readline_read_init_file_impl(PyObject *module, PyObject *filename_obj) |
|
/*[clinic end generated code: output=8e059b676142831e input=62b767adfab6cc15]*/ |
|
{ |
|
PyObject *filename_bytes; |
|
if (filename_obj != Py_None) { |
|
if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) |
|
return NULL; |
|
if (PySys_Audit("open", "OCi", filename_obj, 'r', 0) < 0) { |
|
return NULL; |
|
} |
|
errno = rl_read_init_file(PyBytes_AS_STRING(filename_bytes)); |
|
Py_DECREF(filename_bytes); |
|
} else { |
|
/* We have the choice to either try to exactly reproduce the |
|
* logic to find the filename, ignore it, or provide a dummy value. |
|
* In contract to the history file manipulations, there's no |
|
* clear default to choose. */ |
|
if (PySys_Audit("open", "sCi", "<readline_init_file>", 'r', 0) < 0) { |
|
return NULL; |
|
} |
|
errno = rl_read_init_file(NULL); |
|
} |
|
if (errno) |
|
return PyErr_SetFromErrno(PyExc_OSError); |
|
disable_bracketed_paste(); |
|
Py_RETURN_NONE; |
|
} |
Steps to Reproduce
Build:
CC=clang CXX=clang++ ./configure --disable-optimizations --with-pydebug --with-address-sanitizer && make -j$(nproc)
Run:
./python -X dev -X showrefcount -m test test_audit -j0
Result
LeakSanitizer reports 4 memory leaks of 75 bytes each (300 bytes total). Full logs attached as .log files.
Summary from LeakSanitizer:
SUMMARY: AddressSanitizer: 300 byte(s) leaked in 4 allocation(s).
All leaks originate from PyUnicode_FSConverter calls in the affected functions.
CPython versions tested on:
3.14, 3.15, CPython main branch
Operating systems tested on:
Linux
Linked PRs
Bug report
Bug description:
LeakSanitizer detects memory leaks in the readline module when running
test_audit.Affected Code
Four functions in
Modules/readline.c:PyUnicode_FSConvertercallcpython/Modules/readline.c
Lines 278 to 316 in b2f9fb9
PyUnicode_FSConvertercallcpython/Modules/readline.c
Lines 320 to 366 in b2f9fb9
PyUnicode_FSConvertercallcpython/Modules/readline.c
Lines 371 to 424 in b2f9fb9
PyUnicode_FSConvertercallcpython/Modules/readline.c
Lines 237 to 276 in b2f9fb9
Steps to Reproduce
Build:
Run:
./python -X dev -X showrefcount -m test test_audit -j0Result
LeakSanitizer reports 4 memory leaks of 75 bytes each (300 bytes total). Full logs attached as .log files.
Summary from LeakSanitizer:
All leaks originate from
PyUnicode_FSConvertercalls in the affected functions.CPython versions tested on:
3.14, 3.15, CPython main branch
Operating systems tested on:
Linux
Linked PRs
readlinemodule whenPySys_Auditfails (GH-140400) #140403