diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index d82a4a7f78d..9096665b6ce 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2165,7 +2165,7 @@ class PidTests(unittest.TestCase): class SpawnTests(unittest.TestCase): - def create_args(self, with_env=False): + def create_args(self, with_env=False, use_bytes=False): self.exitcode = 17 filename = support.TESTFN @@ -2185,7 +2185,13 @@ class SpawnTests(unittest.TestCase): with open(filename, "w") as fp: fp.write(code) - return [sys.executable, filename] + args = [sys.executable, filename] + if use_bytes: + args = [os.fsencode(a) for a in args] + self.env = {os.fsencode(k): os.fsencode(v) + for k, v in self.env.items()} + + return args @unittest.skipUnless(hasattr(os, 'spawnl'), 'need os.spawnl') def test_spawnl(self): @@ -2248,6 +2254,13 @@ class SpawnTests(unittest.TestCase): else: self.assertEqual(status, self.exitcode << 8) + @unittest.skipUnless(hasattr(os, 'spawnve'), 'need os.spawnve') + def test_spawnve_bytes(self): + # Test bytes handling in parse_arglist and parse_envlist (#28114) + args = self.create_args(with_env=True, use_bytes=True) + exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env) + self.assertEqual(exitcode, self.exitcode) + # The introduction of this TestCase caused at least two different errors on # *nix buildbots. Temporarily skip this to let the buildbots move along. diff --git a/Misc/NEWS b/Misc/NEWS index 4655b5ea7da..a7ba7d836cf 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Core and Builtins Library ------- +- Issue #28114: Fix a crash in parse_envlist() when env contains byte strings. + Patch by Eryk Sun. + - Issue #27599: Fixed buffer overrun in binascii.b2a_qp() and binascii.a2b_qp(). Build diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 43e3c77cbb2..32d097872bf 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4729,28 +4729,31 @@ free_string_array(EXECV_CHAR **array, Py_ssize_t count) PyMem_DEL(array); } -static -int fsconvert_strdup(PyObject *o, EXECV_CHAR**out) +static int +fsconvert_strdup(PyObject *o, EXECV_CHAR **out) { Py_ssize_t size; + PyObject *ub; + int result = 0; #if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) - *out = PyUnicode_AsWideCharString(o, &size); - if (!*out) + if (!PyUnicode_FSDecoder(o, &ub)) return 0; + *out = PyUnicode_AsWideCharString(ub, &size); + if (*out) + result = 1; #else - PyObject *bytes; - if (!PyUnicode_FSConverter(o, &bytes)) + if (!PyUnicode_FSConverter(o, &ub)) return 0; - size = PyBytes_GET_SIZE(bytes); - *out = PyMem_Malloc(size+1); - if (!*out) { + size = PyBytes_GET_SIZE(ub); + *out = PyMem_Malloc(size + 1); + if (*out) { + memcpy(*out, PyBytes_AS_STRING(ub), size + 1); + result = 1; + } else PyErr_NoMemory(); - return 0; - } - memcpy(*out, PyBytes_AsString(bytes), size+1); - Py_DECREF(bytes); #endif - return 1; + Py_DECREF(ub); + return result; } #endif @@ -4760,7 +4763,7 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) { Py_ssize_t i, pos, envc; PyObject *keys=NULL, *vals=NULL; - PyObject *key, *val, *keyval; + PyObject *key, *val, *key2, *val2, *keyval; EXECV_CHAR **envlist; i = PyMapping_Size(env); @@ -4790,7 +4793,26 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) if (!key || !val) goto error; - keyval = PyUnicode_FromFormat("%U=%U", key, val); +#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) + if (!PyUnicode_FSDecoder(key, &key2)) + goto error; + if (!PyUnicode_FSDecoder(val, &val2)) { + Py_DECREF(key2); + goto error; + } + keyval = PyUnicode_FromFormat("%U=%U", key2, val2); +#else + if (!PyUnicode_FSConverter(key, &key2)) + goto error; + if (!PyUnicode_FSConverter(val, &val2)) { + Py_DECREF(key2); + goto error; + } + keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2), + PyBytes_AS_STRING(val2)); +#endif + Py_DECREF(key2); + Py_DECREF(val2); if (!keyval) goto error; @@ -4798,7 +4820,7 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) Py_DECREF(keyval); goto error; } - + Py_DECREF(keyval); } Py_DECREF(vals);