Issue #29034: Fix memory leak and use-after-free in path_converter.

This commit is contained in:
Xiang Zhang 2017-01-08 23:26:57 +08:00
parent ec53b07ef1
commit 04316c4cc8
2 changed files with 58 additions and 53 deletions

View File

@ -10,6 +10,8 @@ What's New in Python 3.6.1 release candidate 1?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #29034: Fix memory leak and use-after-free in os module (path_converter).
- Issue #29159: Fix regression in bytes(x) when x.__index__() raises Exception. - Issue #29159: Fix regression in bytes(x) when x.__index__() raises Exception.
- Issue #28932: Do not include <sys/random.h> if it does not exist. - Issue #28932: Do not include <sys/random.h> if it does not exist.

View File

@ -785,7 +785,9 @@ dir_fd_converter(PyObject *o, void *p)
* The length of the path in characters, if specified as * The length of the path in characters, if specified as
* a string. * a string.
* path.object * path.object
* The original object passed in. * The original object passed in (if get a PathLike object,
* the result of PyOS_FSPath() is treated as the original object).
* Own a reference to the object.
* path.cleanup * path.cleanup
* For internal use only. May point to a temporary object. * For internal use only. May point to a temporary object.
* (Pay no attention to the man behind the curtain.) * (Pay no attention to the man behind the curtain.)
@ -836,24 +838,22 @@ typedef struct {
#endif #endif
static void static void
path_cleanup(path_t *path) { path_cleanup(path_t *path)
if (path->cleanup) { {
Py_CLEAR(path->cleanup); Py_CLEAR(path->object);
} Py_CLEAR(path->cleanup);
} }
static int static int
path_converter(PyObject *o, void *p) path_converter(PyObject *o, void *p)
{ {
path_t *path = (path_t *)p; path_t *path = (path_t *)p;
PyObject *bytes, *to_cleanup = NULL; PyObject *bytes = NULL;
Py_ssize_t length; Py_ssize_t length = 0;
int is_index, is_buffer, is_bytes, is_unicode; int is_index, is_buffer, is_bytes, is_unicode;
/* Default to failure, forcing explicit signaling of succcess. */
int ret = 0;
const char *narrow; const char *narrow;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
PyObject *wo; PyObject *wo = NULL;
const wchar_t *wide; const wchar_t *wide;
#endif #endif
@ -870,7 +870,9 @@ path_converter(PyObject *o, void *p)
} }
/* Ensure it's always safe to call path_cleanup(). */ /* Ensure it's always safe to call path_cleanup(). */
path->cleanup = NULL; path->object = path->cleanup = NULL;
/* path->object owns a reference to the original object */
Py_INCREF(o);
if ((o == Py_None) && path->nullable) { if ((o == Py_None) && path->nullable) {
path->wide = NULL; path->wide = NULL;
@ -879,10 +881,8 @@ path_converter(PyObject *o, void *p)
#else #else
path->narrow = NULL; path->narrow = NULL;
#endif #endif
path->length = 0;
path->object = o;
path->fd = -1; path->fd = -1;
return 1; goto success_exit;
} }
/* Only call this here so that we don't treat the return value of /* Only call this here so that we don't treat the return value of
@ -899,10 +899,11 @@ path_converter(PyObject *o, void *p)
func = _PyObject_LookupSpecial(o, &PyId___fspath__); func = _PyObject_LookupSpecial(o, &PyId___fspath__);
if (NULL == func) { if (NULL == func) {
goto error_exit; goto error_format;
} }
/* still owns a reference to the original object */
o = to_cleanup = PyObject_CallFunctionObjArgs(func, NULL); Py_DECREF(o);
o = _PyObject_CallNoArg(func);
Py_DECREF(func); Py_DECREF(func);
if (NULL == o) { if (NULL == o) {
goto error_exit; goto error_exit;
@ -914,7 +915,7 @@ path_converter(PyObject *o, void *p)
is_bytes = 1; is_bytes = 1;
} }
else { else {
goto error_exit; goto error_format;
} }
} }
@ -922,26 +923,24 @@ path_converter(PyObject *o, void *p)
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
wide = PyUnicode_AsUnicodeAndSize(o, &length); wide = PyUnicode_AsUnicodeAndSize(o, &length);
if (!wide) { if (!wide) {
goto exit; goto error_exit;
} }
if (length > 32767) { if (length > 32767) {
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
goto exit; goto error_exit;
} }
if (wcslen(wide) != length) { if (wcslen(wide) != length) {
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s"); FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
goto exit; goto error_exit;
} }
path->wide = wide; path->wide = wide;
path->length = length; path->narrow = FALSE;
path->object = o;
path->fd = -1; path->fd = -1;
ret = 1; goto success_exit;
goto exit;
#else #else
if (!PyUnicode_FSConverter(o, &bytes)) { if (!PyUnicode_FSConverter(o, &bytes)) {
goto exit; goto error_exit;
} }
#endif #endif
} }
@ -961,16 +960,16 @@ path_converter(PyObject *o, void *p)
path->nullable ? "string, bytes, os.PathLike or None" : path->nullable ? "string, bytes, os.PathLike or None" :
"string, bytes or os.PathLike", "string, bytes or os.PathLike",
Py_TYPE(o)->tp_name)) { Py_TYPE(o)->tp_name)) {
goto exit; goto error_exit;
} }
bytes = PyBytes_FromObject(o); bytes = PyBytes_FromObject(o);
if (!bytes) { if (!bytes) {
goto exit; goto error_exit;
} }
} }
else if (is_index) { else if (is_index) {
if (!_fd_converter(o, &path->fd)) { if (!_fd_converter(o, &path->fd)) {
goto exit; goto error_exit;
} }
path->wide = NULL; path->wide = NULL;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -978,13 +977,10 @@ path_converter(PyObject *o, void *p)
#else #else
path->narrow = NULL; path->narrow = NULL;
#endif #endif
path->length = 0; goto success_exit;
path->object = o;
ret = 1;
goto exit;
} }
else { else {
error_exit: error_format:
PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s", PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s",
path->function_name ? path->function_name : "", path->function_name ? path->function_name : "",
path->function_name ? ": " : "", path->function_name ? ": " : "",
@ -995,15 +991,14 @@ path_converter(PyObject *o, void *p)
path->nullable ? "string, bytes, os.PathLike or None" : path->nullable ? "string, bytes, os.PathLike or None" :
"string, bytes or os.PathLike", "string, bytes or os.PathLike",
Py_TYPE(o)->tp_name); Py_TYPE(o)->tp_name);
goto exit; goto error_exit;
} }
length = PyBytes_GET_SIZE(bytes); length = PyBytes_GET_SIZE(bytes);
narrow = PyBytes_AS_STRING(bytes); narrow = PyBytes_AS_STRING(bytes);
if ((size_t)length != strlen(narrow)) { if ((size_t)length != strlen(narrow)) {
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s"); FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
Py_DECREF(bytes); goto error_exit;
goto exit;
} }
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -1012,43 +1007,51 @@ path_converter(PyObject *o, void *p)
length length
); );
if (!wo) { if (!wo) {
goto exit; goto error_exit;
} }
wide = PyUnicode_AsWideCharString(wo, &length); wide = PyUnicode_AsUnicodeAndSize(wo, &length);
Py_DECREF(wo);
if (!wide) { if (!wide) {
goto exit; goto error_exit;
} }
if (length > 32767) { if (length > 32767) {
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
goto exit; goto error_exit;
} }
if (wcslen(wide) != length) { if (wcslen(wide) != length) {
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s"); FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
goto exit; goto error_exit;
} }
path->wide = wide; path->wide = wide;
path->narrow = TRUE; path->narrow = TRUE;
path->cleanup = wo;
Py_DECREF(bytes);
#else #else
path->wide = NULL; path->wide = NULL;
path->narrow = narrow; path->narrow = narrow;
#endif
path->length = length;
path->object = o;
path->fd = -1;
if (bytes == o) { if (bytes == o) {
/* Still a reference owned by path->object, don't have to
worry about path->narrow is used after free. */
Py_DECREF(bytes); Py_DECREF(bytes);
ret = 1;
} }
else { else {
path->cleanup = bytes; path->cleanup = bytes;
ret = Py_CLEANUP_SUPPORTED;
} }
exit: #endif
Py_XDECREF(to_cleanup); path->fd = -1;
return ret;
success_exit:
path->length = length;
path->object = o;
return Py_CLEANUP_SUPPORTED;
error_exit:
Py_XDECREF(o);
Py_XDECREF(bytes);
#ifdef MS_WINDOWS
Py_XDECREF(wo);
#endif
return 0;
} }
static void static void