-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Prevent concurrent access to data in InMemoryPerProcess' resolveXXX methods #3216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent concurrent access to data in InMemoryPerProcess' resolveXXX methods #3216
Conversation
461a999
to
40888dc
Compare
@airween: I took a look at the multithreaded example you mentioned here (and adapted it to align with the similar code in the Do you want me to upload it as part of this PR both as an example and also if you want to test some other rules to stress test this code? |
4e4c044
to
3718eca
Compare
3718eca
to
c51500c
Compare
@eduar-hte - please let us know if you finished the work on this PR. Btw: have you seen the Sonarcloud's notifications? |
I think I'm done with the changes, as I tried to limit them to introduce the There is still this question I tried to ask you in this previous comment about whether we should add the multithreaded example you were working on in the context of that issue and that I reworked to align with the changes to the
Yes, I went through all of them. Most of them ( Some of them ( |
c51500c
to
3d9027b
Compare
Yes, I think that would be fine - thanks! |
@eduar-hte sorry, seems you've already uploaded with commit 3d9027b - thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - thanks for this very impressive PR.
No, those were updates to the existing example, I've just uploaded your test from #3054 (and rewritten it to use libModSecurity C++ API and |
df25d02
to
da2bc50
Compare
- As reported in owasp-modsecurity#3054, the resolve methods in InMemoryPerProcess are not acquiring a lock/mutex to prevent concurrent access to the data structures that may be modified at the same time from other threads, and thus triggering undefined behaviour. - Replace inheritance of std::unordered_multimap data structure with data member to prevent potential clients to use it without acquiring the mutex to protect concurrent access. - Replace pthreads lock with std C++11 std::shared_mutex - Provides exclusive/shared lock access so that multiple readers can access the data at the same time, but only one writer. this is used to favor query performance by allowing more concurrent access to the data until an update needs to be performed. - Simplifies acquisition and disposal of lock/mutex with std::lock_guard, which has RAII semantics. - NOTE: Because std::shared_mutex is not recursive, calls to another function that tries to acquire the lock will fail. Introduced __store & __updateFirst helper methods to workaround this. - Updates to InMemoryPerProcess::resolveFirst - Updated the code to store the expired var in 'expiredVars' to delete them after iterating over the range (and releasing the read lock, as 'delIfExpired' needs to acquire it for exclusive access), as the current call to 'delIfExpired' would invalidate the range triggering undefined behaviour on the following iteration. - Noticed that in commit 118e1b3 the call to 'delIfExpired' in this function is done using 'it->second.getValue()'' instead of 'it->first', which seems incorrect (based on similar code in other resolveXXX functions). - Updated InMemoryPerProcess::delIfExpired to use 'std::find_if' (with a lambda that matches both the key and the 'isExpired' condition) because the data structure is a multimap. The version introduced in commit 118e1b3 could find an entry (not necessarily the first, because the map is unordered) where 'isExpired' is 'false' and exit, while another entry could be expired.
…other platforms - Replaced WITHOUT_XXX build options with WITH_XXX to make it easier to understand and configure. - Updated GitHub workflow to align with these changes and include a build 'with lmdb' (again, analogous to non-Windows configurations)
- Replaced pthread_mutex_t in modsecurity::operators::Pm with std::mutex - Replaced pthread's thread usage in reading_logs_via_rule_message example with std::thread. - Simplified and modernized C++ code. - Removed unnecessary includes of pthread.h
…rween) - Rewritten to use C++ libModSecurity API and std::thread (instead of pthreads)
da2bc50
to
4bf9616
Compare
|
@airween - this is now done. when this is merged, issue #3054 should be closed. |
what
Lock access to
InMemoryPerProcess
' data inresolveXXX
methods.why
As reported in #3054, the
resolveXXX
methods inInMemoryPerProcess
are not acquiring a lock/mutex to prevent concurrent access to the data structures that may be modified at the same time from other threads, and thus triggering undefined behaviour.changes
std::unordered_multimap
data structure with data member to prevent potential clients to use it without acquiring the mutex to protect concurrent access.InMemoryPerProcess::resolveFirst
expiredVars
to delete them after iterating over the range (and releasing the read lock, asdelIfExpired
needs to acquire it for exclusive access), as the current call todelIfExpired
would invalidate the range triggering undefined behaviour on the followingiteration.delIfExpired
in this function is done usingit->second.getValue()
instead ofit->first
, which seems incorrect (based on similar code in otherresolveXXX
functions).InMemoryPerProcess::delIfExpired
to usestd::find_if
(with a lambda that matches both the key and theisExpired
condition) because the data structure is a multimap. The version introduced in commit 118e1b3 could find an entry (not necessarily the first, because the map is unordered) whereisExpired
isfalse
and exit, while another entry could be expired.std::shared_mutex
std::shared_mutex
is not recursive, calls to another function that tries to acquire the lock will fail. Introduced__store
&__updateFirst
helper methods to workaround this.