From 3330bc01cbdecb12be9c30d78f648710f374d23e Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Wed, 18 Sep 2019 08:46:58 +0000 Subject: [PATCH] Fix race condition when getting cmdline Even though we check for the existence of the process before, it may still terminate before we get to read its command line, so catch the error that can occur here. Change-Id: I3e89aca8bedfd2912effe2490718223f7d03133e Closes-Bug: 1844500 --- neutron/agent/linux/utils.py | 27 ++++++++++++-------- neutron/tests/unit/agent/linux/test_utils.py | 10 ++++++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/neutron/agent/linux/utils.py b/neutron/agent/linux/utils.py index 79640877dd6..7f8566f4110 100644 --- a/neutron/agent/linux/utils.py +++ b/neutron/agent/linux/utils.py @@ -323,19 +323,24 @@ def process_is_running(pid): def get_cmdline_from_pid(pid): if not process_is_running(pid): return [] - with open('/proc/%s/cmdline' % pid, 'r') as f: - cmdline = f.readline().split('\0')[:-1] + # NOTE(jh): Even after the above check, the process may terminate + # before the open below happens + try: + with open('/proc/%s/cmdline' % pid, 'r') as f: + cmdline = f.readline().split('\0')[:-1] + except IOError: + return [] - # NOTE(slaweq): sometimes it may happen that values in - # /proc/{pid}/cmdline are separated by space instead of NUL char, - # in such case we would have everything in one element of cmdline_args - # list and it would not match to expected cmd so we need to try to - # split it by spaces - if len(cmdline) == 1: - cmdline = cmdline[0].split(' ') + # NOTE(slaweq): sometimes it may happen that values in + # /proc/{pid}/cmdline are separated by space instead of NUL char, + # in such case we would have everything in one element of cmdline_args + # list and it would not match to expected cmd so we need to try to + # split it by spaces + if len(cmdline) == 1: + cmdline = cmdline[0].split(' ') - LOG.debug("Found cmdline %s for process with PID %s.", cmdline, pid) - return cmdline + LOG.debug("Found cmdline %s for process with PID %s.", cmdline, pid) + return cmdline def cmd_matches_expected(cmd, expected_cmd): diff --git a/neutron/tests/unit/agent/linux/test_utils.py b/neutron/tests/unit/agent/linux/test_utils.py index 50c27e119d7..bb816f0ec39 100644 --- a/neutron/tests/unit/agent/linux/test_utils.py +++ b/neutron/tests/unit/agent/linux/test_utils.py @@ -336,6 +336,16 @@ class TestGetCmdlineFromPid(base.BaseTestCase): mock_open.assert_not_called() self.assertEqual([], cmdline) + def test_cmdline_process_disappearing(self): + self.process_is_running_mock.return_value = True + mock_open = self.useFixture( + lib_fixtures.OpenFixture('/proc/%s/cmdline' % self.pid, 'process') + ).mock_open + mock_open.side_effect = IOError() + cmdline = utils.get_cmdline_from_pid(self.pid) + mock_open.assert_called_once_with('/proc/%s/cmdline' % self.pid, 'r') + self.assertEqual([], cmdline) + class TestFindChildPids(base.BaseTestCase):