diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py index fe0c928e1..df3a7e3f0 100644 --- a/qutebrowser/utils/standarddir.py +++ b/qutebrowser/utils/standarddir.py @@ -163,8 +163,11 @@ def _get(typ): # non-existant an attempt should be made to create it with permission # 0700. If the destination directory exists already the permissions # should not be changed. - if path is not None and not os.path.exists(path): - os.makedirs(path, 0o700) + if path is not None: + try: + os.makedirs(path, 0o700) + except FileExistsError: + pass return path diff --git a/tests/unit/utils/test_standarddir.py b/tests/unit/utils/test_standarddir.py index 1fe9bb67d..1302034e1 100644 --- a/tests/unit/utils/test_standarddir.py +++ b/tests/unit/utils/test_standarddir.py @@ -316,3 +316,23 @@ class TestCreatingDir: if os.name == 'posix': assert basedir.stat().mode & 0o777 == 0o700 + + @pytest.mark.parametrize('typ', DIR_TYPES) + def test_exists_race_condition(self, mocker, tmpdir, typ): + """Make sure there can't be a TOCTOU issue when creating the file. + + See https://github.com/The-Compiler/qutebrowser/issues/942. + """ + (tmpdir / typ).ensure(dir=True) + + m = mocker.patch('qutebrowser.utils.standarddir.os') + m.makedirs = os.makedirs + m.sep = os.sep + m.path.join = os.path.join + m.path.exists.return_value = False + + args = types.SimpleNamespace(basedir=str(tmpdir)) + standarddir.init(args) + + func = getattr(standarddir, typ) + func()