diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 4c620ccae9c8..aa7b9ff7ce03 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2472,6 +2472,22 @@ def test_waitpid(self): status = os.waitpid(pid, 0) self.assertEqual(status, (pid, 0)) + @unittest.skipUnless(hasattr(os, 'wait3'), "os.wait3() is required") + def test_wait3_rusage_initialized(self): + # Ensure a successful wait3() call where no child was ready to report + # its exit status does not return uninitialized memory in the rusage + # structure. See bpo-36279. + args = [sys.executable, '-c', 'import sys; sys.stdin.read()'] + proc = subprocess.Popen(args, stdin=subprocess.PIPE) + try: + pid, status, rusage = os.wait3(os.WNOHANG) + self.assertEqual(0, pid) + self.assertEqual(0, status) + self.assertEqual(0, sum(rusage)) + finally: + proc.stdin.close() + proc.wait() + class SpawnTests(unittest.TestCase): def create_args(self, *, with_env=False, use_bytes=False): diff --git a/Misc/NEWS.d/next/Library/2019-03-13-03-56-36.bpo-36279.4CC01n.rst b/Misc/NEWS.d/next/Library/2019-03-13-03-56-36.bpo-36279.4CC01n.rst new file mode 100644 index 000000000000..69afbcd1be67 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-03-13-03-56-36.bpo-36279.4CC01n.rst @@ -0,0 +1,2 @@ +The :func:`os.wait3` function could return uninitialized memory if the system +call succeeded, but no child process exit status was reported. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 3f760183575a..abfc692c2b2d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7354,6 +7354,12 @@ wait_helper(pid_t pid, int status, struct rusage *ru) if (pid == -1) return posix_error(); + /* If wait succeeded but no child was ready to report status, ru will not + * have been populated. Clear it to avoid returning uninitialized stack to + * the caller. */ + if (pid == 0) + memset(ru, 0, sizeof(*ru)); + if (struct_rusage == NULL) { PyObject *m = PyImport_ImportModuleNoBlock("resource"); if (m == NULL)