From b5df8c5942c6447e8cb743fddc3d2178cfea7313 Mon Sep 17 00:00:00 2001 From: Jean Guyomarc'h Date: Mon, 4 Nov 2019 11:17:49 +0100 Subject: [PATCH] fix: TOCTOU race when creating directories When create_directories() is called to create a chain of directories, path components are created one by one. For each path component, ghc checks if the directory exists. If not it attempts to create it. In case of failure to create this directory, the function fails. This behavior exhibits a TOCTOU (Time Of Check/Time Of Use) race when different threads of execution (e.g. different processes) attempt to create the same paths (or paths that contain a common hierarchy). The following sequence of events between threads T1 and T2 illustrates the issue: T1: checks path P exists: no. Will attempt to create P... T2: checks path P exists: no. Will attempt to create P... T1: creates P, no error T2: fails to create P: raises an error It is not desirable for create_directories() to fail in this case. This commit mirrors the GNU libstdc++ implementation of the c++ filesystem library: if the creation of a directory fails, we will inspect the path that should have been created as a directory. If it is indeed a directory (another thread of execution created it for us), the error is now reset and the function continues to iterate to the next path component. --- include/ghc/filesystem.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index f926d38..a8b6f8a 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -3391,7 +3391,12 @@ GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept if (!exists(fs)) { create_directory(current, ec); if (ec) { - return false; + std::error_code tmp_ec; + if (is_directory(current, tmp_ec)) { + ec.clear(); + } else { + return false; + } } } #ifndef LWG_2935_BEHAVIOUR