From 81a7a002b4bff69eabde42da4bfd5e318a3be745 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 21 Apr 2016 19:03:57 +0200 Subject: [PATCH] Improve validation for flow control statements fixes #11622 --- lib/config/config_parser.yy | 260 +++++++++++++++++++++++++++------- lib/config/configcompiler.hpp | 8 ++ 2 files changed, 216 insertions(+), 52 deletions(-) diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 7ff14df67..6a0e4df69 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -62,6 +62,8 @@ do { \ fputs(str.c_str(), file); \ } while (0) +#define YYINITDEPTH 10000 + using namespace icinga; template @@ -184,12 +186,14 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %type lterm_items_inner %type rterm %type rterm_array +%type rterm_dict %type rterm_scope_require_side_effect %type rterm_scope %type else_if_branches %type else_if_branch %type rterm_side_effect %type rterm_no_side_effect +%type rterm_no_side_effect_no_dict %type lterm %type object %type apply @@ -238,6 +242,27 @@ void yyerror(const YYLTYPE *locp, std::vector int yyparse(std::vector > *llist, ConfigCompiler *context); +static void BeginFlowControlBlock(ConfigCompiler *compiler, int allowedTypes, bool inherit) +{ + if (inherit) + allowedTypes |= compiler->m_FlowControlInfo.top(); + + compiler->m_FlowControlInfo.push(allowedTypes); +} + +static void EndFlowControlBlock(ConfigCompiler *compiler) +{ + compiler->m_FlowControlInfo.pop(); +} + +static void UseFlowControl(ConfigCompiler *compiler, FlowControlType type, const CompilerDebugInfo& location) +{ + int fci = compiler->m_FlowControlInfo.top(); + + if ((type & fci) != type) + BOOST_THROW_EXCEPTION(ScriptError("Invalid flow control statement.", location)); +} + Expression *ConfigCompiler::Compile(void) { std::vector > llist; @@ -245,10 +270,12 @@ Expression *ConfigCompiler::Compile(void) //yydebug = 1; m_IgnoreNewlines.push(false); + BeginFlowControlBlock(this, 0, false); if (yyparse(&llist, this) != 0) return NULL; + EndFlowControlBlock(this); m_IgnoreNewlines.pop(); std::vector dlist; @@ -344,8 +371,14 @@ object: context->m_Assign.push(0); context->m_Ignore.push(0); } - object_declaration identifier optional_rterm use_specifier ignore_specifier rterm_scope_require_side_effect + object_declaration identifier optional_rterm use_specifier ignore_specifier { + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope_require_side_effect + { + EndFlowControlBlock(context); + context->m_ObjectAssign.pop(); bool abstract = $2; @@ -353,8 +386,6 @@ object: String type = *$3; delete $3; - $7->MakeInline(); - bool seen_assign = context->m_SeenAssign.top(); context->m_SeenAssign.pop(); @@ -386,7 +417,7 @@ object: BOOST_THROW_EXCEPTION(ScriptError("object rule 'ignore' is missing 'assign' for type '" + type + "'", DebugInfoRange(@2, @4))); } - $$ = new ObjectExpression(abstract, type, $4, filter, context->GetZone(), context->GetPackage(), $5, $6, $7, DebugInfoRange(@2, @6)); + $$ = new ObjectExpression(abstract, type, $4, filter, context->GetZone(), context->GetPackage(), $5, $6, $8, DebugInfoRange(@2, @6)); } ; @@ -474,8 +505,30 @@ lterm: T_LIBRARY rterm { $$ = new ImportExpression($2, @$); } - | T_ASSIGN T_WHERE rterm + | T_ASSIGN T_WHERE { + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope %dprec 2 + { + EndFlowControlBlock(context); + + if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top())) + BOOST_THROW_EXCEPTION(ScriptError("'assign' keyword not valid in this context.", @$)); + + context->m_SeenAssign.top() = true; + + if (context->m_Assign.top()) + context->m_Assign.top() = new LogicalOrExpression(context->m_Assign.top(), $4, @$); + else + context->m_Assign.top() = $4; + + $$ = MakeLiteral(); + } + | T_ASSIGN T_WHERE rterm %dprec 1 + { + ASSERT(!dynamic_cast($3)); + if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top())) BOOST_THROW_EXCEPTION(ScriptError("'assign' keyword not valid in this context.", @$)); @@ -488,8 +541,30 @@ lterm: T_LIBRARY rterm $$ = MakeLiteral(); } - | T_IGNORE T_WHERE rterm + | T_IGNORE T_WHERE { + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope %dprec 2 + { + EndFlowControlBlock(context); + + if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top())) + BOOST_THROW_EXCEPTION(ScriptError("'ignore' keyword not valid in this context.", @$)); + + context->m_SeenIgnore.top() = true; + + if (context->m_Ignore.top()) + context->m_Ignore.top() = new LogicalOrExpression(context->m_Ignore.top(), $4, @$); + else + context->m_Ignore.top() = $4; + + $$ = MakeLiteral(); + } + | T_IGNORE T_WHERE rterm %dprec 1 + { + ASSERT(!dynamic_cast($3)); + if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top())) BOOST_THROW_EXCEPTION(ScriptError("'ignore' keyword not valid in this context.", @$)); @@ -504,14 +579,17 @@ lterm: T_LIBRARY rterm } | T_RETURN optional_rterm { + UseFlowControl(context, FlowControlReturn, @$); $$ = new ReturnExpression($2, @$); } | T_BREAK { + UseFlowControl(context, FlowControlBreak, @$); $$ = new BreakExpression(@$); } | T_CONTINUE { + UseFlowControl(context, FlowControlContinue, @$); $$ = new ContinueExpression(@$); } | T_DEBUGGER @@ -520,26 +598,38 @@ lterm: T_LIBRARY rterm } | apply | object - | T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' rterm_scope_require_side_effect + | T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' { - $9->MakeInline(); + BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true); + } + rterm_scope_require_side_effect + { + EndFlowControlBlock(context); - $$ = new ForExpression(*$3, *$5, $7, $9, @$); + $$ = new ForExpression(*$3, *$5, $7, $10, @$); delete $3; delete $5; } - | T_FOR '(' identifier T_IN rterm ')' rterm_scope_require_side_effect + | T_FOR '(' identifier T_IN rterm ')' { - $7->MakeInline(); + BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true); + } + rterm_scope_require_side_effect + { + EndFlowControlBlock(context); - $$ = new ForExpression(*$3, "", $5, $7, @$); + $$ = new ForExpression(*$3, "", $5, $8, @$); delete $3; } - | T_FUNCTION identifier '(' identifier_items ')' use_specifier rterm_scope + | T_FUNCTION identifier '(' identifier_items ')' use_specifier { - $7->MakeInline(); + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope + { + EndFlowControlBlock(context); - FunctionExpression *fexpr = new FunctionExpression(*$4, $6, $7, @$); + FunctionExpression *fexpr = new FunctionExpression(*$4, $6, $8, @$); delete $4; $$ = new SetExpression(MakeIndexer(ScopeThis, *$2), OpSetLiteral, fexpr, @$); @@ -562,11 +652,15 @@ lterm: T_LIBRARY rterm BindToScope(expr, ScopeLocal); $$ = new SetExpression(expr, $3, $4, @$); } - | T_WHILE '(' rterm ')' rterm_scope + | T_WHILE '(' rterm ')' { - $5->MakeInline(); + BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true); + } + rterm_scope + { + EndFlowControlBlock(context); - $$ = new WhileExpression($3, $5, @$); + $$ = new WhileExpression($3, $6, @$); } | T_THROW rterm { @@ -613,11 +707,37 @@ rterm_array: '[' } rterm_items ']' { + context->m_OpenBraces--; $$ = new ArrayExpression(*$3, @$); delete $3; } ; +rterm_dict: '{' + { + BeginFlowControlBlock(context, 0, false); + context->m_IgnoreNewlines.push(false); + context->m_OpenBraces++; + } + statements '}' + { + EndFlowControlBlock(context); + context->m_OpenBraces--; + context->m_IgnoreNewlines.pop(); + std::vector dlist; + typedef std::pair EListItem; + int num = 0; + BOOST_FOREACH(const EListItem& litem, *$3) { + if (!litem.second.SideEffect) + yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used."); + dlist.push_back(litem.first); + num++; + } + delete $3; + $$ = new DictExpression(dlist, @$); + } + ; + rterm_scope_require_side_effect: '{' { context->m_IgnoreNewlines.push(false); @@ -638,6 +758,7 @@ rterm_scope_require_side_effect: '{' } delete $3; $$ = new DictExpression(dlist, @$); + $$->MakeInline(); } ; @@ -661,13 +782,12 @@ rterm_scope: '{' } delete $3; $$ = new DictExpression(dlist, @$); + $$->MakeInline(); } ; else_if_branch: T_ELSE T_IF '(' rterm ')' rterm_scope { - $6->MakeInline(); - $$ = new std::pair($4, $6); } ; @@ -691,8 +811,6 @@ rterm_side_effect: rterm '(' rterm_items ')' } | T_IF '(' rterm ')' rterm_scope else_if_branches { - $5->MakeInline(); - std::vector > ebranches = *$6; delete $6; @@ -707,8 +825,6 @@ rterm_side_effect: rterm '(' rterm_items ')' } | T_IF '(' rterm ')' rterm_scope else_if_branches T_ELSE rterm_scope { - $5->MakeInline(); - std::vector > ebranches = *$6; delete $6; @@ -725,7 +841,7 @@ rterm_side_effect: rterm '(' rterm_items ')' } ; -rterm_no_side_effect: T_STRING +rterm_no_side_effect_no_dict: T_STRING { $$ = MakeLiteral(*$1); delete $1; @@ -792,11 +908,23 @@ rterm_no_side_effect: T_STRING { $$ = MakeLiteral(@$.FirstLine); } - | identifier T_FOLLOWS rterm + | identifier T_FOLLOWS { - DictExpression *aexpr = dynamic_cast($3); - if (aexpr) - aexpr->MakeInline(); + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope %dprec 2 + { + EndFlowControlBlock(context); + + std::vector args; + args.push_back(*$1); + delete $1; + + $$ = new FunctionExpression(args, new std::map(), $4, @$); + } + | identifier T_FOLLOWS rterm %dprec 1 + { + ASSERT(!dynamic_cast($3)); std::vector args; args.push_back(*$1); @@ -804,22 +932,25 @@ rterm_no_side_effect: T_STRING $$ = new FunctionExpression(args, new std::map(), $3, @$); } - | '(' identifier_items ')' T_FOLLOWS rterm + | '(' identifier_items ')' T_FOLLOWS { - DictExpression *aexpr = dynamic_cast($5); - if (aexpr) - aexpr->MakeInline(); + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope %dprec 2 + { + EndFlowControlBlock(context); + + $$ = new FunctionExpression(*$2, new std::map(), $6, @$); + delete $2; + } + | '(' identifier_items ')' T_FOLLOWS rterm %dprec 1 + { + ASSERT(!dynamic_cast($5)); $$ = new FunctionExpression(*$2, new std::map(), $5, @$); delete $2; } | rterm_array - | rterm_scope_require_side_effect - { - Expression *expr = $1; - BindToScope(expr, ScopeThis); - $$ = expr; - } | '(' { context->m_OpenBraces++; @@ -849,25 +980,35 @@ rterm_no_side_effect: T_STRING | rterm T_DIVIDE_OP rterm { MakeRBinaryOp(&$$, $1, $3, @1, @3); } | rterm T_MODULO rterm { MakeRBinaryOp(&$$, $1, $3, @1, @3); } | rterm T_XOR rterm { MakeRBinaryOp(&$$, $1, $3, @1, @3); } - | T_FUNCTION '(' identifier_items ')' use_specifier rterm_scope + | T_FUNCTION '(' identifier_items ')' use_specifier { - $6->MakeInline(); + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope + { + EndFlowControlBlock(context); - $$ = new FunctionExpression(*$3, $5, $6, @$); + $$ = new FunctionExpression(*$3, $5, $7, @$); delete $3; } - | T_NULLARY_LAMBDA_BEGIN statements T_NULLARY_LAMBDA_END + | T_NULLARY_LAMBDA_BEGIN { + BeginFlowControlBlock(context, FlowControlReturn, false); + } + statements T_NULLARY_LAMBDA_END + { + EndFlowControlBlock(context); + std::vector dlist; typedef std::pair EListItem; int num = 0; - BOOST_FOREACH(const EListItem& litem, *$2) { - if (!litem.second.SideEffect && num != $2->size() - 1) + BOOST_FOREACH(const EListItem& litem, *$3) { + if (!litem.second.SideEffect && num != $3->size() - 1) yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used."); dlist.push_back(litem.first); num++; } - delete $2; + delete $3; DictExpression *aexpr = new DictExpression(dlist, @$); aexpr->MakeInline(); @@ -875,8 +1016,19 @@ rterm_no_side_effect: T_STRING } ; -rterm: rterm_side_effect - | rterm_no_side_effect +rterm_no_side_effect: + rterm_no_side_effect_no_dict %dprec 1 + | rterm_dict %dprec 2 + { + Expression *expr = $1; + BindToScope(expr, ScopeThis); + $$ = expr; + } + ; + +rterm: + rterm_side_effect %dprec 2 + | rterm_no_side_effect %dprec 1 ; target_type_specifier: /* empty */ @@ -975,8 +1127,14 @@ apply: context->m_FVVar.push(""); context->m_FTerm.push(NULL); } - T_APPLY identifier optional_rterm apply_for_specifier target_type_specifier use_specifier ignore_specifier rterm_scope_require_side_effect + T_APPLY identifier optional_rterm apply_for_specifier target_type_specifier use_specifier ignore_specifier { + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope_require_side_effect + { + EndFlowControlBlock(context); + context->m_Apply.pop(); String type = *$3; @@ -1008,8 +1166,6 @@ apply: BOOST_THROW_EXCEPTION(ScriptError("'apply' target type '" + target + "' is invalid", DebugInfoRange(@2, @5))); } - $9->MakeInline(); - bool seen_assign = context->m_SeenAssign.top(); context->m_SeenAssign.pop(); @@ -1047,7 +1203,7 @@ apply: Expression *fterm = context->m_FTerm.top(); context->m_FTerm.pop(); - $$ = new ApplyExpression(type, target, $4, filter, context->GetPackage(), fkvar, fvvar, fterm, $7, $8, $9, DebugInfoRange(@2, @8)); + $$ = new ApplyExpression(type, target, $4, filter, context->GetPackage(), fkvar, fvvar, fterm, $7, $8, $10, DebugInfoRange(@2, @8)); } ; diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp index 860098ed3..e595a91c3 100644 --- a/lib/config/configcompiler.hpp +++ b/lib/config/configcompiler.hpp @@ -64,6 +64,13 @@ struct EItemInfo CompilerDebugInfo DebugInfo; }; +enum FlowControlType +{ + FlowControlReturn = 1, + FlowControlContinue = 2, + FlowControlBreak = 4 +}; + struct ZoneFragment { String Tag; @@ -156,6 +163,7 @@ public: std::stack m_FKVar; std::stack m_FVVar; std::stack m_FTerm; + std::stack m_FlowControlInfo; }; }