-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Fix memory leak #3470
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
base: v3/master
Are you sure you want to change the base?
Fix memory leak #3470
Conversation
d9c7725 to
4b360b3
Compare
The members m_variableArgsNames, m_variableArgsGetNames, and m_variableArgsPostNames were allocated using new but stored as references. This caused a memory leak because their destructors were never called. This change wraps the allocations in std::unique_ptr to ensure proper cleanup while keeping the reference members to maintain compatibility with existing code syntax.
Valgrind reported memory leaks in m_ruleRemoveTargetByTag and related containers when ctl actions were used. This change explicitly clears m_ruleRemoveById, m_ruleRemoveByIdRange, m_ruleRemoveByTag, m_ruleRemoveTargetByTag, and m_ruleRemoveTargetById in the Transaction destructor to ensure all allocated memory is freed.
|
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.
Pull request overview
This PR aims to address a reported memory leak during nginx reloads (ModSecurity issue #2848) by adjusting Transaction cleanup and changing how TransactionAnchoredVariables stores AnchoredSetVariableTranslationProxy instances.
Changes:
- Explicitly clears several rule-removal tracking lists in
Transaction::~Transaction(). - Refactors
TransactionAnchoredVariablesto allocateAnchoredSetVariableTranslationProxyobjects on the heap viastd::unique_ptr, with public reference aliases preserving existing access patterns.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/transaction.cc |
Adds explicit clear() calls for rule-removal lists during Transaction destruction. |
headers/modsecurity/transaction.h |
Replaces by-value translation proxy members with unique_ptr ownership plus reference aliases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsNames; | ||
| AnchoredSetVariableTranslationProxy &m_variableArgsNames; | ||
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsGetNames; | ||
| AnchoredSetVariableTranslationProxy &m_variableArgsGetNames; | ||
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsPostNames; |
Copilot
AI
Jan 25, 2026
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.
These new m_pVariableArgs* members are public and become part of the TransactionAnchoredVariables/Transaction API surface and object layout. If the intent is just to manage lifetime internally, consider keeping ownership private (e.g., make the pointer members private and expose only the existing m_variableArgs*Names fields or accessors), or avoid introducing the extra pointer members altogether by storing the proxies by value.
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsNames; | |
| AnchoredSetVariableTranslationProxy &m_variableArgsNames; | |
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsGetNames; | |
| AnchoredSetVariableTranslationProxy &m_variableArgsGetNames; | |
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsPostNames; | |
| private: | |
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsNames; | |
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsGetNames; | |
| std::unique_ptr<AnchoredSetVariableTranslationProxy> m_pVariableArgsPostNames; | |
| public: | |
| AnchoredSetVariableTranslationProxy &m_variableArgsNames; | |
| AnchoredSetVariableTranslationProxy &m_variableArgsGetNames; |
| m_pVariableArgsNames(std::make_unique<AnchoredSetVariableTranslationProxy>("ARGS_NAMES", &m_variableArgs)), | ||
| m_variableArgsNames(*m_pVariableArgsNames), | ||
| m_pVariableArgsGetNames(std::make_unique<AnchoredSetVariableTranslationProxy>("ARGS_GET_NAMES", &m_variableArgsGet)), | ||
| m_variableArgsGetNames(*m_pVariableArgsGetNames), | ||
| m_pVariableArgsPostNames(std::make_unique<AnchoredSetVariableTranslationProxy>("ARGS_POST_NAMES", &m_variableArgsPost)), |
Copilot
AI
Jan 25, 2026
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.
PR description states m_variableArgs*Names were previously references bound to heap allocations (so destructors couldn’t free them). In this header, they were value members before this change, and this diff actually introduces reference members + heap allocation. Can you clarify the leak reproduction and why switching to heap allocation is necessary here? As-is, this change doesn’t match the described root cause.
| m_pVariableArgsNames(std::make_unique<AnchoredSetVariableTranslationProxy>("ARGS_NAMES", &m_variableArgs)), | ||
| m_variableArgsNames(*m_pVariableArgsNames), | ||
| m_pVariableArgsGetNames(std::make_unique<AnchoredSetVariableTranslationProxy>("ARGS_GET_NAMES", &m_variableArgsGet)), | ||
| m_variableArgsGetNames(*m_pVariableArgsGetNames), | ||
| m_pVariableArgsPostNames(std::make_unique<AnchoredSetVariableTranslationProxy>("ARGS_POST_NAMES", &m_variableArgsPost)), | ||
| m_variableArgsPostNames(*m_pVariableArgsPostNames) |
Copilot
AI
Jan 25, 2026
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.
The new m_pVariable* + &m_variable* pattern adds three heap allocations per Transaction and is a fairly unusual ownership model (a unique_ptr plus a public reference alias). Previously these proxies were stored by value and would have been destroyed automatically with TransactionAnchoredVariables, so this change is unlikely to affect object lifetime; it mainly adds indirection/allocations. Consider keeping these as value members, or (if indirection is truly needed) store only std::unique_ptr members and update call sites to use .get()/* rather than introducing reference data members.
|
first of all, thank you for this PR. It was a bit suspicious, for eg. I don't understand the idea behind the new constructor's variable creating (you pass a non-existent reference to the new pointer type variable), but before I continue the understanding of the code, please could you show me a rule (a SecRule) or a configuration snippet and a request where I can check this memleak (with Valgrind). Thank you! |



This is a potential fix to #2848
It was AI generated by Google AI Studio
Here are it's workings:
Based on the Valgrind output and the provided code, the memory leak is located in the
TransactionAnchoredVariablesclass (which is part of theTransactionobject).The stack trace indicates that memory allocated during the construction of
AnchoredSetVariableTranslationProxy(specifically inside astd::functioninitialization) is never freed.In
src/transaction.cc, theTransactionconstructor initializesTransactionAnchoredVariables:And in
src/variables/variable.h, the code accessesm_variableArgsNamesas if it were an object (using&), not a pointer:This usage pattern combined with the leak indicates that
TransactionAnchoredVariablesdeclares its members (likem_variableArgsNames) as references (&) but initializes them usingnewin its constructor.Because they are references, the
TransactionAnchoredVariablesdestructor does not (and cannot) delete the objects they refer to. Consequently, theAnchoredSetVariableTranslationProxyobjects (and their internalstd::functionmembers) are leaked when theTransactionis destroyed.To fix this:
The members in
TransactionAnchoredVariablesshould be changed from references (e.g.,AnchoredSetVariableTranslationProxy &) to pointers (e.g.,std::unique_ptr<AnchoredSetVariableTranslationProxy>), or they should be deleted manually in theTransactionAnchoredVariablesdestructor if raw pointers are used. Given the&t->memberusage, they were likely intended to be value members or references binding to dynamic memory (which is the bug).