mirror of
https://github.com/python/cpython
synced 2024-10-14 10:48:24 +00:00
gh-104522: Fix OSError raised when run a subprocess (#114195)
Only set filename to cwd if it was caused by failed chdir(cwd). _fork_exec() now returns "noexec:chdir" for failed chdir(cwd). Co-authored-by: Robert O'Shea <PurityLake@users.noreply.github.com>
This commit is contained in:
parent
4c7e09d012
commit
e2c097ebde
|
@ -1944,16 +1944,21 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
|
||||||
SubprocessError)
|
SubprocessError)
|
||||||
if issubclass(child_exception_type, OSError) and hex_errno:
|
if issubclass(child_exception_type, OSError) and hex_errno:
|
||||||
errno_num = int(hex_errno, 16)
|
errno_num = int(hex_errno, 16)
|
||||||
child_exec_never_called = (err_msg == "noexec")
|
if err_msg == "noexec:chdir":
|
||||||
if child_exec_never_called:
|
|
||||||
err_msg = ""
|
err_msg = ""
|
||||||
# The error must be from chdir(cwd).
|
# The error must be from chdir(cwd).
|
||||||
err_filename = cwd
|
err_filename = cwd
|
||||||
|
elif err_msg == "noexec":
|
||||||
|
err_msg = ""
|
||||||
|
err_filename = None
|
||||||
else:
|
else:
|
||||||
err_filename = orig_executable
|
err_filename = orig_executable
|
||||||
if errno_num != 0:
|
if errno_num != 0:
|
||||||
err_msg = os.strerror(errno_num)
|
err_msg = os.strerror(errno_num)
|
||||||
raise child_exception_type(errno_num, err_msg, err_filename)
|
if err_filename is not None:
|
||||||
|
raise child_exception_type(errno_num, err_msg, err_filename)
|
||||||
|
else:
|
||||||
|
raise child_exception_type(errno_num, err_msg)
|
||||||
raise child_exception_type(err_msg)
|
raise child_exception_type(err_msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2017,11 +2017,12 @@ def test_user(self):
|
||||||
"import os; print(os.getuid())"],
|
"import os; print(os.getuid())"],
|
||||||
user=user,
|
user=user,
|
||||||
close_fds=close_fds)
|
close_fds=close_fds)
|
||||||
except PermissionError: # (EACCES, EPERM)
|
except PermissionError as e: # (EACCES, EPERM)
|
||||||
pass
|
self.assertIsNone(e.filename)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (errno.EACCES, errno.EPERM):
|
if e.errno not in (errno.EACCES, errno.EPERM):
|
||||||
raise
|
raise
|
||||||
|
self.assertIsNone(e.filename)
|
||||||
else:
|
else:
|
||||||
if isinstance(user, str):
|
if isinstance(user, str):
|
||||||
user_uid = pwd.getpwnam(user).pw_uid
|
user_uid = pwd.getpwnam(user).pw_uid
|
||||||
|
@ -2065,8 +2066,8 @@ def test_group(self):
|
||||||
"import os; print(os.getgid())"],
|
"import os; print(os.getgid())"],
|
||||||
group=group,
|
group=group,
|
||||||
close_fds=close_fds)
|
close_fds=close_fds)
|
||||||
except PermissionError: # (EACCES, EPERM)
|
except PermissionError as e: # (EACCES, EPERM)
|
||||||
pass
|
self.assertIsNone(e.filename)
|
||||||
else:
|
else:
|
||||||
if isinstance(group, str):
|
if isinstance(group, str):
|
||||||
group_gid = grp.getgrnam(group).gr_gid
|
group_gid = grp.getgrnam(group).gr_gid
|
||||||
|
@ -2114,7 +2115,8 @@ def _test_extra_groups_impl(self, *, gid, group_list):
|
||||||
[sys.executable, "-c",
|
[sys.executable, "-c",
|
||||||
"import os, sys, json; json.dump(os.getgroups(), sys.stdout)"],
|
"import os, sys, json; json.dump(os.getgroups(), sys.stdout)"],
|
||||||
extra_groups=group_list)
|
extra_groups=group_list)
|
||||||
except PermissionError:
|
except PermissionError as e:
|
||||||
|
self.assertIsNone(e.filename)
|
||||||
self.skipTest("setgroup() EPERM; this test may require root.")
|
self.skipTest("setgroup() EPERM; this test may require root.")
|
||||||
else:
|
else:
|
||||||
parent_groups = os.getgroups()
|
parent_groups = os.getgroups()
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
:exc:`OSError` raised when run a subprocess now only has *filename*
|
||||||
|
attribute set to *cwd* if the error was caused by a failed attempt to change
|
||||||
|
the current directory.
|
|
@ -673,9 +673,10 @@ child_exec(char *const exec_array[],
|
||||||
PyObject *preexec_fn,
|
PyObject *preexec_fn,
|
||||||
PyObject *preexec_fn_args_tuple)
|
PyObject *preexec_fn_args_tuple)
|
||||||
{
|
{
|
||||||
int i, saved_errno, reached_preexec = 0;
|
int i, saved_errno;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
const char* err_msg = "";
|
/* Indicate to the parent that the error happened before exec(). */
|
||||||
|
const char *err_msg = "noexec";
|
||||||
/* Buffer large enough to hold a hex integer. We can't malloc. */
|
/* Buffer large enough to hold a hex integer. We can't malloc. */
|
||||||
char hex_errno[sizeof(saved_errno)*2+1];
|
char hex_errno[sizeof(saved_errno)*2+1];
|
||||||
|
|
||||||
|
@ -735,8 +736,12 @@ child_exec(char *const exec_array[],
|
||||||
/* We no longer manually close p2cread, c2pwrite, and errwrite here as
|
/* We no longer manually close p2cread, c2pwrite, and errwrite here as
|
||||||
* _close_open_fds takes care when it is not already non-inheritable. */
|
* _close_open_fds takes care when it is not already non-inheritable. */
|
||||||
|
|
||||||
if (cwd)
|
if (cwd) {
|
||||||
POSIX_CALL(chdir(cwd));
|
if (chdir(cwd) == -1) {
|
||||||
|
err_msg = "noexec:chdir";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (child_umask >= 0)
|
if (child_umask >= 0)
|
||||||
umask(child_umask); /* umask() always succeeds. */
|
umask(child_umask); /* umask() always succeeds. */
|
||||||
|
@ -784,7 +789,7 @@ child_exec(char *const exec_array[],
|
||||||
#endif /* HAVE_SETREUID */
|
#endif /* HAVE_SETREUID */
|
||||||
|
|
||||||
|
|
||||||
reached_preexec = 1;
|
err_msg = "";
|
||||||
if (preexec_fn != Py_None && preexec_fn_args_tuple) {
|
if (preexec_fn != Py_None && preexec_fn_args_tuple) {
|
||||||
/* This is where the user has asked us to deadlock their program. */
|
/* This is where the user has asked us to deadlock their program. */
|
||||||
result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL);
|
result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL);
|
||||||
|
@ -842,16 +847,12 @@ child_exec(char *const exec_array[],
|
||||||
}
|
}
|
||||||
_Py_write_noraise(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur);
|
_Py_write_noraise(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur);
|
||||||
_Py_write_noraise(errpipe_write, ":", 1);
|
_Py_write_noraise(errpipe_write, ":", 1);
|
||||||
if (!reached_preexec) {
|
|
||||||
/* Indicate to the parent that the error happened before exec(). */
|
|
||||||
_Py_write_noraise(errpipe_write, "noexec", 6);
|
|
||||||
}
|
|
||||||
/* We can't call strerror(saved_errno). It is not async signal safe.
|
/* We can't call strerror(saved_errno). It is not async signal safe.
|
||||||
* The parent process will look the error message up. */
|
* The parent process will look the error message up. */
|
||||||
} else {
|
} else {
|
||||||
_Py_write_noraise(errpipe_write, "SubprocessError:0:", 18);
|
_Py_write_noraise(errpipe_write, "SubprocessError:0:", 18);
|
||||||
_Py_write_noraise(errpipe_write, err_msg, strlen(err_msg));
|
|
||||||
}
|
}
|
||||||
|
_Py_write_noraise(errpipe_write, err_msg, strlen(err_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue