From 30eea293730d16ee214dfc6edb9fdbed434becde Mon Sep 17 00:00:00 2001 From: dw Date: Mon, 4 Feb 2019 18:52:26 +0000 Subject: [PATCH] Avoid accidental fork bombs due to try/except (#51633) (#51636) WorkerProcess exceptions could previously cause StrategyBase loop to resume in every child. See ticket description. --- lib/ansible/executor/process/worker.py | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/ansible/executor/process/worker.py b/lib/ansible/executor/process/worker.py index 32ea239a96..c8f780113e 100644 --- a/lib/ansible/executor/process/worker.py +++ b/lib/ansible/executor/process/worker.py @@ -87,7 +87,40 @@ class WorkerProcess(multiprocessing.Process): # set to /dev/null self._new_stdin = os.devnull + def _hard_exit(self, e): + ''' + There is no safe exception to return to higher level code that does not + risk an innocent try/except finding itself executing in the wrong + process. All code executing above WorkerProcess.run() on the stack + conceptually belongs to another program. + ''' + + try: + display.debug(u"WORKER HARD EXIT: %s" % to_text(e)) + except BaseException: + # If the cause of the fault is IOError being generated by stdio, + # attempting to log a debug message may trigger another IOError. + # Try printing once then give up. + pass + + os._exit(1) + def run(self): + ''' + Wrap _run() to ensure no possibility an errant exception can cause + control to return to the StrategyBase task loop, or any other code + higher in the stack. + + As multiprocessing in Python 2.x provides no protection, it is possible + a try/except added in far-away code can cause a crashed child process + to suddenly assume the role and prior state of its parent. + ''' + try: + return self._run() + except BaseException as e: + self._hard_exit(e) + + def _run(self): ''' Called when the process is started. Pushes the result onto the results queue. We also remove the host from the blocked hosts list, to