gh-106030: Miscellaneous fixes in Python/suggestions.c (GH-106031)

* PyDict_GetItem() and PyObject_HasAttr() suppress arbitrary errors and
  should not be used.
* PyUnicode_CompareWithASCIIString() only works if the second argument
  is ASCII string.
* Refleak in get_suggestions_for_name_error.
* Use of borrowed pointer after possible freeing (self).
* Add some missing error checks.
This commit is contained in:
Serhiy Storchaka 2023-06-23 19:53:27 +03:00 committed by GitHub
parent 9499b0f138
commit c8c162ef52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -151,15 +151,15 @@ calculate_suggestions(PyObject *dir,
} }
for (int i = 0; i < dir_size; ++i) { for (int i = 0; i < dir_size; ++i) {
PyObject *item = PyList_GET_ITEM(dir, i); PyObject *item = PyList_GET_ITEM(dir, i);
if (_PyUnicode_Equal(name, item)) {
continue;
}
Py_ssize_t item_size; Py_ssize_t item_size;
const char *item_str = PyUnicode_AsUTF8AndSize(item, &item_size); const char *item_str = PyUnicode_AsUTF8AndSize(item, &item_size);
if (item_str == NULL) { if (item_str == NULL) {
PyMem_Free(buffer); PyMem_Free(buffer);
return NULL; return NULL;
} }
if (PyUnicode_CompareWithASCIIString(name, item_str) == 0) {
continue;
}
// No more than 1/3 of the involved characters should need changed. // No more than 1/3 of the involved characters should need changed.
Py_ssize_t max_distance = (name_size + item_size + 3) * MOVE_COST / 6; Py_ssize_t max_distance = (name_size + item_size + 3) * MOVE_COST / 6;
// Don't take matches we've already beaten. // Don't take matches we've already beaten.
@ -220,37 +220,48 @@ get_suggestions_for_name_error(PyObject* name, PyFrameObject* frame)
assert(code != NULL && code->co_localsplusnames != NULL); assert(code != NULL && code->co_localsplusnames != NULL);
PyObject *varnames = _PyCode_GetVarnames(code); PyObject *varnames = _PyCode_GetVarnames(code);
Py_DECREF(code);
if (varnames == NULL) { if (varnames == NULL) {
return NULL; return NULL;
} }
PyObject *dir = PySequence_List(varnames); PyObject *dir = PySequence_List(varnames);
Py_DECREF(varnames); Py_DECREF(varnames);
Py_DECREF(code);
if (dir == NULL) { if (dir == NULL) {
return NULL; return NULL;
} }
// Are we inside a method and the instance has an attribute called 'name'? // Are we inside a method and the instance has an attribute called 'name'?
if (PySequence_Contains(dir, &_Py_ID(self)) > 0) { int res = PySequence_Contains(dir, &_Py_ID(self));
if (res < 0) {
goto error;
}
if (res > 0) {
PyObject* locals = PyFrame_GetLocals(frame); PyObject* locals = PyFrame_GetLocals(frame);
if (!locals) { if (!locals) {
goto error; goto error;
} }
PyObject* self = PyDict_GetItem(locals, &_Py_ID(self)); /* borrowed */ PyObject* self = PyDict_GetItemWithError(locals, &_Py_ID(self)); /* borrowed */
Py_DECREF(locals);
if (!self) { if (!self) {
Py_DECREF(locals);
goto error; goto error;
} }
if (PyObject_HasAttr(self, name)) { PyObject *value;
res = _PyObject_LookupAttr(self, name, &value);
Py_DECREF(locals);
if (res < 0) {
goto error;
}
if (value) {
Py_DECREF(value);
Py_DECREF(dir); Py_DECREF(dir);
return PyUnicode_FromFormat("self.%S", name); return PyUnicode_FromFormat("self.%U", name);
} }
} }
PyObject *suggestions = calculate_suggestions(dir, name); PyObject *suggestions = calculate_suggestions(dir, name);
Py_DECREF(dir); Py_DECREF(dir);
if (suggestions != NULL) { if (suggestions != NULL || PyErr_Occurred()) {
return suggestions; return suggestions;
} }
@ -260,7 +271,7 @@ get_suggestions_for_name_error(PyObject* name, PyFrameObject* frame)
} }
suggestions = calculate_suggestions(dir, name); suggestions = calculate_suggestions(dir, name);
Py_DECREF(dir); Py_DECREF(dir);
if (suggestions != NULL) { if (suggestions != NULL || PyErr_Occurred()) {
return suggestions; return suggestions;
} }
@ -319,15 +330,16 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc)
assert(frame != NULL); assert(frame != NULL);
PyObject* suggestion = get_suggestions_for_name_error(name, frame); PyObject* suggestion = get_suggestions_for_name_error(name, frame);
bool is_stdlib_module = is_name_stdlib_module(name); if (suggestion == NULL && PyErr_Occurred()) {
if (suggestion == NULL && !is_stdlib_module) {
return NULL; return NULL;
} }
// Add a trailer ". Did you mean: (...)?" // Add a trailer ". Did you mean: (...)?"
PyObject* result = NULL; PyObject* result = NULL;
if (!is_stdlib_module) { if (!is_name_stdlib_module(name)) {
if (suggestion == NULL) {
return NULL;
}
result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion); result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion);
} else if (suggestion == NULL) { } else if (suggestion == NULL) {
result = PyUnicode_FromFormat(". Did you forget to import %R?", name); result = PyUnicode_FromFormat(". Did you forget to import %R?", name);