Skip to content

Commit 16233fd

Browse files
eduar-htegberkes
authored and
gberkes
committed
Fixed shared files deadlock in a multi-threaded Windows application
- The shared files Windows implementation introduced in PR owasp-modsecurity#3132 works in multi-process single-threaded contexts but it doesn't work correctly in single-process multi-threaded contexts. - The issue is that the LockFileEx Win32 function works on a per-handle basis. - In a multi-process context, each process will have called SharedFiles::add_new_handler when initializing the SharedFile and obtained a handle, and thus locking will work. - When running ModSecurity in a single process using multiple threads, the initialization of the SharedFile will happen once and the handle will be shared by all threads. Then, if two threads try to write to the same shared file concurrently, they may deadlock as one of them will lock the file (by calling LockFileEx) and then proceed to write to the file. If before writing to the file and unlocking it, another thread calls LockFileEx on the same handle, the attempt to write to the file will lock generating a deadlock. - The new implementation replaces usage of LockFileEx/UnlockFileEx with a named mutex to lock access to the shared file. - A named mutex is used to support multi-process scenarios. - The mutex name is generated using the filename to support multiple shared files (such as that for the debug and audit logs). - This assumes that both process will initialize the SharedFile instance using the same filename (which is expected as they'd be using the same configuration file)
1 parent 2a4950c commit 16233fd

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

src/utils/shared_files.cc

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
#include <fcntl.h>
1919
#ifdef WIN32
20-
#include <windows.h>
21-
#include <io.h>
20+
#include <algorithm>
2221
#endif
2322

2423

@@ -34,7 +33,32 @@ SharedFiles::handlers_map::iterator SharedFiles::add_new_handler(
3433
return m_handlers.end();
3534
}
3635

37-
return m_handlers.insert({ fileName, {fp, 0} }).first;
36+
#ifdef WIN32
37+
// replace invalid characters for a Win32 named object
38+
auto tmp = fileName;
39+
std::replace(tmp.begin(), tmp.end(), '\\', '_');
40+
std::replace(tmp.begin(), tmp.end(), '/', '_');
41+
42+
// use named mutex for multi-process locking support
43+
const auto mutexName = "Global\\ModSecurity_" + tmp;
44+
45+
HANDLE hMutex = CreateMutex(NULL, FALSE, mutexName.c_str());
46+
if (hMutex == NULL) {
47+
error->assign("Failed to create mutex for shared file: " + fileName);
48+
fclose(fp);
49+
return m_handlers.end();
50+
}
51+
#endif
52+
53+
auto handler = handler_info {
54+
fp,
55+
#ifdef WIN32
56+
hMutex,
57+
#endif
58+
0
59+
};
60+
// cppcheck-suppress resourceLeak ; false positive, fp is closed in SharedFiles::close
61+
return m_handlers.insert({ fileName, handler }).first;
3862
}
3963

4064

@@ -69,6 +93,9 @@ void SharedFiles::close(const std::string& fileName) {
6993
if (it->second.cnt == 0)
7094
{
7195
fclose(it->second.fp);
96+
#ifdef WIN32
97+
CloseHandle(it->second.hMutex);
98+
#endif
7299

73100
m_handlers.erase(it);
74101
}
@@ -92,9 +119,11 @@ bool SharedFiles::write(const std::string& fileName,
92119
lock.l_type = F_WRLCK;
93120
fcntl(fileno(it->second.fp), F_SETLKW, &lock);
94121
#else
95-
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fileno(it->second.fp)));
96-
OVERLAPPED overlapped = { 0 };
97-
::LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped);
122+
DWORD dwWaitResult = WaitForSingleObject(it->second.hMutex, INFINITE);
123+
if (dwWaitResult != WAIT_OBJECT_0) {
124+
error->assign("couldn't lock shared file: " + fileName);
125+
return false;
126+
}
98127
#endif
99128

100129
auto wrote = fwrite(msg.c_str(), 1, msg.size(), it->second.fp);
@@ -109,8 +138,7 @@ bool SharedFiles::write(const std::string& fileName,
109138
lock.l_type = F_UNLCK;
110139
fcntl(fileno(it->second.fp), F_SETLKW, &lock);
111140
#else
112-
overlapped = { 0 };
113-
::UnlockFileEx(handle, 0, MAXDWORD, MAXDWORD, &overlapped);
141+
::ReleaseMutex(it->second.hMutex);
114142
#endif
115143

116144
return ret;

src/utils/shared_files.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919

2020
#include <stdio.h>
21+
#ifdef WIN32
22+
#include <Windows.h>
23+
#endif
2124

2225
#include <unordered_map>
2326
#include <string>
@@ -53,6 +56,9 @@ class SharedFiles {
5356

5457
struct handler_info {
5558
FILE* fp;
59+
#ifdef WIN32
60+
HANDLE hMutex;
61+
#endif
5662
unsigned int cnt;
5763
};
5864

0 commit comments

Comments
 (0)