Skip to content

Commit daf288e

Browse files
committed
gh-151416: Don't mask non-TypeError argv conversion errors in os.spawnv
os.spawnv() replaced any error raised during argv item conversion, such as MemoryError, codec errors, or the embedded-null ValueError, with a generic TypeError. Only add the contextual message when the conversion actually raised TypeError, matching how os.spawnve() and the exec functions propagate these errors. The test is gated to the native C spawnv: the Python fallback used elsewhere reports conversion failures from the forked child as exit status 127 instead of raising.
1 parent 8da22af commit daf288e

3 files changed

Lines changed: 39 additions & 4 deletions

File tree

Lib/test/test_os/test_os.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ def requires_os_func(name):
8686
return unittest.skipUnless(hasattr(os, name), 'requires os.%s' % name)
8787

8888

89+
# On platforms without a native spawnv(), os.py provides a Python fallback
90+
# built on fork()+exec*() that reports argument conversion failures from the
91+
# child as exit status 127 instead of raising, so tests of the C
92+
# implementation's error paths cannot run against it.
93+
requires_native_spawnv = unittest.skipUnless(
94+
isinstance(getattr(os, 'spawnv', None), types.BuiltinFunctionType),
95+
'requires the native C os.spawnv')
96+
97+
8998
# bpo-41625: On AIX, splice() only works with a socket, not with a pipe.
9099
requires_splice_pipe = unittest.skipIf(sys.platform.startswith("aix"),
91100
'on AIX, splice() only accepts sockets')
@@ -3502,6 +3511,25 @@ def test_spawnve_noargs(self):
35023511
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, ('',), {})
35033512
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, [''], {})
35043513

3514+
@requires_native_spawnv
3515+
def test_spawnv_arg_conversion_errors(self):
3516+
# A non-path argv item gets a TypeError naming the argument...
3517+
with self.assertRaisesRegex(TypeError, 'must contain only strings'):
3518+
os.spawnv(os.P_NOWAIT, sys.executable, [sys.executable, 123])
3519+
# ...but other conversion errors must not be masked as TypeError
3520+
# (gh-151416).
3521+
with self.assertRaises(ValueError):
3522+
os.spawnv(os.P_NOWAIT, sys.executable,
3523+
[sys.executable, 'embedded\0null'])
3524+
3525+
class RaisingPath:
3526+
def __fspath__(self):
3527+
raise RuntimeError('gotcha')
3528+
3529+
with self.assertRaisesRegex(RuntimeError, 'gotcha'):
3530+
os.spawnv(os.P_NOWAIT, sys.executable,
3531+
[sys.executable, RaisingPath()])
3532+
35053533
def _test_invalid_env(self, spawn):
35063534
program = sys.executable
35073535
args = self.quote_args([program, '-c', 'pass'])
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
Fix a crash in :func:`os.spawnv` and :func:`os.spawnve` when an *argv*
22
item's :meth:`~os.PathLike.__fspath__` method mutates the *argv* list
3-
during argument conversion.
3+
during argument conversion. :func:`!os.spawnv` argument conversion errors
4+
other than :exc:`TypeError`, such as the :exc:`ValueError` for an embedded
5+
null, are no longer replaced with a generic :exc:`TypeError`.

Modules/posixmodule.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8230,9 +8230,14 @@ os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv)
82308230
if (!fsconvert_strdup(item, &argvlist[i])) {
82318231
Py_DECREF(item);
82328232
free_string_array(argvlist, i);
8233-
PyErr_SetString(
8234-
PyExc_TypeError,
8235-
"spawnv() arg 2 must contain only strings");
8233+
// Add argument context to the converter's terse TypeError, but
8234+
// let MemoryError, codec errors, embedded-null ValueError, etc.
8235+
// propagate unmasked.
8236+
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
8237+
PyErr_SetString(
8238+
PyExc_TypeError,
8239+
"spawnv() arg 2 must contain only strings");
8240+
}
82368241
return NULL;
82378242
}
82388243
Py_DECREF(item);

0 commit comments

Comments
 (0)