-
Notifications
You must be signed in to change notification settings - Fork 380
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
Protect Processor::Context#run with a mutex #2773
Conversation
d26a468
to
00f8033
Compare
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.
Makes sense. Feel free to 🚢 once Steep is happy
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.
This seems reasonable, but it's a very small/subtle code change for a potential big issue. Thus, I would suggest perhaps leaving a few comments in the code explaining why this tiny addition is important, otherwise this may get hard to maintain in the future (since people would need to partially "reverse engineer" why this was added here and what it's protecting exactly).
@run_mutex.lock | ||
|
||
start_ns = Core::Utils::Time.get_time(:nanosecond) |
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.
Minor: Consider using @run_mutex.synchronize
-- I think that one's a bit easier to maintain and "break" with future changes.
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.
Rationale:
- blocks carry a cost: def/ensure/end is faster
- it makes the function return value less explicit (you have to konw/remember/lookup that synchronize does return the block value)
- it creates whitespace churn on the code change
- maybe I'm doing too much Go but def/ensure/end is a common pattern that matches Go's
defer
;)
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.
Added a comment to clarify the intent!
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.
Thanks!
00f8033
to
20718a5
Compare
fd72683
to
2994528
Compare
@time_ns += res.total_runtime | ||
@time_ext_ns += (stop_ns - start_ns) | ||
@timeouts += 1 if res.timeout | ||
|
||
res | ||
ensure | ||
@run_mutex.unlock | ||
end | ||
|
||
def finalize |
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.
Just a final question -- does @context.finalize
not need protecting too?
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.
Nope, that one is fine.
Context initialization and finalization happen on the same thread and should outlive any transient thread that would theoretically use the same context (you'd get much bigger problems otherwise, that a simple synchronize would not solve). It's all theoretical anyway because no code is currently allowing getting the context from another thread, so this is mostly bulletproofing following a discussion around run
.
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
What does this PR do?
Protect Processor::Context#run with a mutex
Motivation
ddwaf_run
mutatesddwaf_context
in a non-thread-safe way.Additional Notes
Sicne there is a 1:1:1 context - request - web worker thread mapping it should very rarely happen in typical apps so the lock will just fly through.
This aims to protect from code that spawns threads within a request and that would theoretically trigger a context run (e.g ActionController::Live). There is currently none but there could be in the future.
How to test the change?
Difficult since there's no such real case currently. Even synthetically (e.g in a spec) it would be hard as it would require producing a reliably failing case of libddwaf stepping onto itself.