fix(parser): add destructor directives to prevent memory leaks#3500
fix(parser): add destructor directives to prevent memory leaks#3500airween merged 4 commits intoowasp-modsecurity:v3/masterfrom
Conversation
Fixes owasp-modsecurity#3448 The Bison parser was missing %destructor directives for all semantic types using unique_ptr. When parsing errors occurred (via YYERROR), the parser would discard semantic values from the stack without properly destroying them, causing memory leaks detected by ASAN. Added %destructor directives for: - std::unique_ptr<actions::Action> - std::unique_ptr<RunTimeString> - std::unique_ptr<std::vector<std::unique_ptr<actions::Action>>> - std::unique_ptr<Operator> - std::unique_ptr<Variable> - std::unique_ptr<std::vector<std::unique_ptr<Variable>>> The empty braces {} in the destructor directives are intentional. With C++ variant semantic values (api.value.type variant), Bison automatically calls the destructors of objects in the variant when discarding semantic values during error recovery. This fixes memory leaks that occurred in scenarios such as: 1. When ACTION_INIT macro fails and calls YYERROR (lines 885, 892) 2. When rule creation fails and calls YYERROR (line 1130) 3. Any other parsing error that causes the parser to discard semantic values during error recovery
There was a problem hiding this comment.
Pull request overview
Adds Bison %destructor directives for C++ variant semantic values that hold std::unique_ptr, aiming to ensure semantic values are properly cleaned up when the parser discards symbols during error recovery (e.g., via YYERROR), addressing ASAN-reported leaks (Fixes #3448).
Changes:
- Introduces
%destructordirectives for severalstd::unique_ptrsemantic types in the Seclang Bison grammar. - Adds inline comments documenting the intent and rationale for empty destructor bodies under
api.value.type variant.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Felipe Zipitria <felipe.zipitria@owasp.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The previous commit added %destructor directives but used inconsistent spacing in nested template types. The %type declarations used spaces between closing angle brackets (> > > >) but %destructor used (>>>>), causing Bison to not associate the destructors with any symbols. Modern Bison 3.8.2 detected this with warnings: "type <...> is used, but is not associated to any symbol" This meant vector type destructors were silently ignored, leaving those memory leaks from issue owasp-modsecurity#3448 unfixed. Fixed by matching the spacing exactly between %type and %destructor declarations. Also enhanced comments to explain why destructor bodies are empty when using api.value.type variant.
91bb152 to
d405133
Compare
|
The #line directives referencing "seclang-parser.tab.cc" are cosmetic and fine. Here's why: What #line directives do:
Why it says .tab.cc even though the file is .cc:
|
Signed-off-by: Felipe Zipitria <felipe.zipitria@owasp.org>
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
src/parser/seclang-scanner.cc:8694
num_to_readwas changed toyy_size_t, but it's computed from anintexpression (yy_buf_size - number_to_move - 1). If that expression is negative, it will wrap to a huge unsigned value and thewhile (num_to_read <= 0)growth loop will never run, risking an out-of-bounds write inYY_INPUT. Keep this value in a signed type (e.g.,int/ptrdiff_t) or restructure the calculation so the “no space left” condition is checked before converting to an unsigned type.
yy_size_t num_to_read =
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
while ( num_to_read <= 0 )
{ /* Not enough room in the buffer - grow it. */
src/parser/seclang-scanner.cc:8709
- The overflow/resize guard here no longer works as intended after switching
new_sizetoyy_size_t:if (new_size <= 0)is effectively dead code for a size type, so the fallback growth path is unreachable. Consider keepingnew_sizesigned (or computing inyy_size_tand checking for overflow explicitly) so buffer growth remains correct for largeyy_buf_sizevalues.
yy_size_t new_size = b->yy_buf_size * 2;
if ( new_size <= 0 )
b->yy_buf_size += b->yy_buf_size / 8;
else
b->yy_buf_size *= 2;
src/parser/seclang-scanner.cc:9384
- This
yylessredefinition was updated to useyy_size_t, but the earlier#define yyless(n)(near line ~215) still usesint yyless_macro_arg = (n);. Withyylengnow beingyy_size_t, calls likeyyless(yyleng)can still truncate via the first macro definition. Update the firstyylessmacro to useyy_size_tas well to keep behavior consistent.
#undef yyless
#define yyless(n) \
do \
{ \
/* Undo effects of setting up yytext. */ \
yy_size_t yyless_macro_arg = (n); \
YY_LESS_LINENO(yyless_macro_arg);\
yytext[yyleng] = (yy_hold_char); \
(yy_c_buf_p) = yytext + yyless_macro_arg; \
(yy_hold_char) = *(yy_c_buf_p); \
*(yy_c_buf_p) = '\0'; \
yyleng = yyless_macro_arg; \
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thank you @fzipi! |

what
The Bison parser was missing
%destructordirectives for all semantic types usingunique_ptr. When parsing errors occurred (via YYERROR), the parser would discard semantic values from the stack without properly destroying them, causing memory leaks detected by ASAN.why
Added %destructor directives for:
The empty braces {} in the destructor directives are intentional. With C++ variant semantic values (api.value.type variant), Bison automatically calls the destructors of objects in the variant when discarding semantic values during error recovery.
This fixes memory leaks that occurred in scenarios such as:
references