From: Steve Dower Date: Sun, 20 Nov 2016 03:17:26 +0000 (-0800) Subject: Issue #28732: Raise ValueError when argv[0] is empty X-Git-Tag: v3.6.0b4~50 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bce26262d1b4873e2f9a3da69f638ba56d36ce89;p=thirdparty%2FPython%2Fcpython.git Issue #28732: Raise ValueError when argv[0] is empty --- bce26262d1b4873e2f9a3da69f638ba56d36ce89 diff --cc Lib/test/test_os.py index 9194a8a2beb3,10383cfc1f0e..e9fdb0719f4b --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@@ -1481,8 -1442,8 +1481,16 @@@ class ExecTests(unittest.TestCase) self.assertRaises(OSError, os.execvpe, 'no such app-', ['no such app-'], None) ++ def test_execv_with_bad_arglist(self): ++ self.assertRaises(ValueError, os.execv, 'notepad', ()) ++ self.assertRaises(ValueError, os.execv, 'notepad', []) ++ self.assertRaises(ValueError, os.execv, 'notepad', ('',)) ++ self.assertRaises(ValueError, os.execv, 'notepad', ['']) ++ def test_execvpe_with_bad_arglist(self): self.assertRaises(ValueError, os.execvpe, 'notepad', [], None) ++ self.assertRaises(ValueError, os.execvpe, 'notepad', [], {}) ++ self.assertRaises(ValueError, os.execvpe, 'notepad', [''], {}) @unittest.skipUnless(hasattr(os, '_execvpe'), "No internal os._execvpe function to test.") @@@ -2224,125 -2182,6 +2232,131 @@@ class PidTests(unittest.TestCase) self.assertEqual(status, (pid, 0)) +class SpawnTests(unittest.TestCase): + def create_args(self, *, with_env=False, use_bytes=False): + self.exitcode = 17 + + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + if not with_env: + code = 'import sys; sys.exit(%s)' % self.exitcode + else: + self.env = dict(os.environ) + # create an unique key + self.key = str(uuid.uuid4()) + self.env[self.key] = self.key + # read the variable from os.environ to check that it exists + code = ('import sys, os; magic = os.environ[%r]; sys.exit(%s)' + % (self.key, self.exitcode)) + + with open(filename, "w") as fp: + fp.write(code) + + 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 + + @requires_os_func('spawnl') + def test_spawnl(self): + args = self.create_args() + exitcode = os.spawnl(os.P_WAIT, args[0], *args) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnle') + def test_spawnle(self): + args = self.create_args(with_env=True) + exitcode = os.spawnle(os.P_WAIT, args[0], *args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnlp') + def test_spawnlp(self): + args = self.create_args() + exitcode = os.spawnlp(os.P_WAIT, args[0], *args) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnlpe') + def test_spawnlpe(self): + args = self.create_args(with_env=True) + exitcode = os.spawnlpe(os.P_WAIT, args[0], *args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnv') + def test_spawnv(self): + args = self.create_args() + exitcode = os.spawnv(os.P_WAIT, args[0], args) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnve') + def test_spawnve(self): + args = self.create_args(with_env=True) + exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnvp') + def test_spawnvp(self): + args = self.create_args() + exitcode = os.spawnvp(os.P_WAIT, args[0], args) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnvpe') + def test_spawnvpe(self): + args = self.create_args(with_env=True) + exitcode = os.spawnvpe(os.P_WAIT, args[0], args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @requires_os_func('spawnv') + def test_nowait(self): + args = self.create_args() + pid = os.spawnv(os.P_NOWAIT, args[0], args) + result = os.waitpid(pid, 0) + self.assertEqual(result[0], pid) + status = result[1] + if hasattr(os, 'WIFEXITED'): + self.assertTrue(os.WIFEXITED(status)) + self.assertEqual(os.WEXITSTATUS(status), self.exitcode) + else: + self.assertEqual(status, self.exitcode << 8) + + @requires_os_func('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) + + @requires_os_func('spawnl') + def test_spawnl_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, args[0]) ++ self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, args[0], '') + + @requires_os_func('spawnle') - def test_spawnl_noargs(self): ++ def test_spawnle_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, args[0], {}) ++ self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, args[0], '', {}) + + @requires_os_func('spawnv') + def test_spawnv_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ()) + self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], []) ++ self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ('',)) ++ self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ['']) + + @requires_os_func('spawnve') - def test_spawnv_noargs(self): ++ def test_spawnve_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], (), {}) + self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [], {}) ++ self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], ('',), {}) ++ self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [''], {}) + # The introduction of this TestCase caused at least two different errors on # *nix buildbots. Temporarily skip this to let the buildbots move along. @unittest.skip("Skip due to platform/environment differences on *NIX buildbots") diff --cc Modules/posixmodule.c index 0482f2bbd034,3e446a524e9e..acef3c31e8b5 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@@ -4916,12 -5065,8 +4916,20 @@@ os_execv_impl(PyObject *module, path_t if (argvlist == NULL) { return NULL; } ++ if (!argvlist[0][0]) { ++ PyErr_SetString(PyExc_ValueError, ++ "execv() arg 2 first element cannot be empty"); ++ free_string_array(argvlist, argc); ++ return NULL; ++ } - execv(path_char, argvlist); ++ _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_WEXECV + _wexecv(path->wide, argvlist); +#else + execv(path->narrow, argvlist); +#endif ++ _Py_END_SUPPRESS_IPH /* If we get here it's definitely an error */ @@@ -4961,6 -5106,6 +4969,11 @@@ os_execve_impl(PyObject *module, path_ goto fail; } argc = PySequence_Size(argv); ++ if (argc < 1) { ++ PyErr_SetString(PyExc_ValueError, "execve: argv must not be empty"); ++ return NULL; ++ } ++ if (!PyMapping_Check(env)) { PyErr_SetString(PyExc_TypeError, "execve: environment must be a mapping object"); @@@ -4971,21 -5116,17 +4984,28 @@@ if (argvlist == NULL) { goto fail; } ++ if (!argvlist[0][0]) { ++ PyErr_SetString(PyExc_ValueError, ++ "execve: argv first element cannot be empty"); ++ goto fail; ++ } envlist = parse_envlist(env, &envc); if (envlist == NULL) goto fail; ++ _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_FEXECVE if (path->fd > -1) fexecve(path->fd, argvlist, envlist); else #endif +#ifdef HAVE_WEXECV + _wexecve(path->wide, argvlist, envlist); +#else execve(path->narrow, argvlist, envlist); +#endif ++ _Py_END_SUPPRESS_IPH /* If we get here it's definitely an error */ @@@ -5061,6 -5210,15 +5081,13 @@@ os_spawnv_impl(PyObject *module, int mo "spawnv() arg 2 must contain only strings"); return NULL; } -#ifdef MS_WINDOWS + if (i == 0 && !argvlist[0][0]) { + free_string_array(argvlist, i); + PyErr_SetString( + PyExc_ValueError, + "spawnv() arg 2 first element cannot be empty"); + return NULL; + } -#endif } argvlist[argc] = NULL; @@@ -5155,6 -5306,6 +5182,13 @@@ os_spawnve_impl(PyObject *module, int m lastarg = i; goto fail_1; } ++ if (i == 0 && !argvlist[0][0]) { ++ lastarg = i; ++ PyErr_SetString( ++ PyExc_ValueError, ++ "spawnv() arg 2 first element cannot be empty"); ++ goto fail_1; ++ } } lastarg = argc; argvlist[argc] = NULL;