Improve validation for flow control statements

fixes #11622
This commit is contained in:
Gunnar Beutner 2016-04-21 19:03:57 +02:00
parent 9a88185b12
commit 81a7a002b4
2 changed files with 216 additions and 52 deletions

View File

@ -62,6 +62,8 @@ do { \
fputs(str.c_str(), file); \ fputs(str.c_str(), file); \
} while (0) } while (0)
#define YYINITDEPTH 10000
using namespace icinga; using namespace icinga;
template<typename T> template<typename T>
@ -184,12 +186,14 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig
%type <llist> lterm_items_inner %type <llist> lterm_items_inner
%type <expr> rterm %type <expr> rterm
%type <expr> rterm_array %type <expr> rterm_array
%type <dexpr> rterm_dict
%type <dexpr> rterm_scope_require_side_effect %type <dexpr> rterm_scope_require_side_effect
%type <dexpr> rterm_scope %type <dexpr> rterm_scope
%type <ebranchlist> else_if_branches %type <ebranchlist> else_if_branches
%type <ebranch> else_if_branch %type <ebranch> else_if_branch
%type <expr> rterm_side_effect %type <expr> rterm_side_effect
%type <expr> rterm_no_side_effect %type <expr> rterm_no_side_effect
%type <expr> rterm_no_side_effect_no_dict
%type <expr> lterm %type <expr> lterm
%type <expr> object %type <expr> object
%type <expr> apply %type <expr> apply
@ -238,6 +242,27 @@ void yyerror(const YYLTYPE *locp, std::vector<std::pair<Expression *, EItemInfo>
int yyparse(std::vector<std::pair<Expression *, EItemInfo> > *llist, ConfigCompiler *context); int yyparse(std::vector<std::pair<Expression *, EItemInfo> > *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) Expression *ConfigCompiler::Compile(void)
{ {
std::vector<std::pair<Expression *, EItemInfo> > llist; std::vector<std::pair<Expression *, EItemInfo> > llist;
@ -245,10 +270,12 @@ Expression *ConfigCompiler::Compile(void)
//yydebug = 1; //yydebug = 1;
m_IgnoreNewlines.push(false); m_IgnoreNewlines.push(false);
BeginFlowControlBlock(this, 0, false);
if (yyparse(&llist, this) != 0) if (yyparse(&llist, this) != 0)
return NULL; return NULL;
EndFlowControlBlock(this);
m_IgnoreNewlines.pop(); m_IgnoreNewlines.pop();
std::vector<Expression *> dlist; std::vector<Expression *> dlist;
@ -344,8 +371,14 @@ object:
context->m_Assign.push(0); context->m_Assign.push(0);
context->m_Ignore.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(); context->m_ObjectAssign.pop();
bool abstract = $2; bool abstract = $2;
@ -353,8 +386,6 @@ object:
String type = *$3; String type = *$3;
delete $3; delete $3;
$7->MakeInline();
bool seen_assign = context->m_SeenAssign.top(); bool seen_assign = context->m_SeenAssign.top();
context->m_SeenAssign.pop(); 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))); 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, @$); $$ = 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<DictExpression *>($3));
if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top())) 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.", @$)); BOOST_THROW_EXCEPTION(ScriptError("'assign' keyword not valid in this context.", @$));
@ -488,8 +541,30 @@ lterm: T_LIBRARY rterm
$$ = MakeLiteral(); $$ = 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<DictExpression *>($3));
if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top())) 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.", @$)); BOOST_THROW_EXCEPTION(ScriptError("'ignore' keyword not valid in this context.", @$));
@ -504,14 +579,17 @@ lterm: T_LIBRARY rterm
} }
| T_RETURN optional_rterm | T_RETURN optional_rterm
{ {
UseFlowControl(context, FlowControlReturn, @$);
$$ = new ReturnExpression($2, @$); $$ = new ReturnExpression($2, @$);
} }
| T_BREAK | T_BREAK
{ {
UseFlowControl(context, FlowControlBreak, @$);
$$ = new BreakExpression(@$); $$ = new BreakExpression(@$);
} }
| T_CONTINUE | T_CONTINUE
{ {
UseFlowControl(context, FlowControlContinue, @$);
$$ = new ContinueExpression(@$); $$ = new ContinueExpression(@$);
} }
| T_DEBUGGER | T_DEBUGGER
@ -520,26 +598,38 @@ lterm: T_LIBRARY rterm
} }
| apply | apply
| object | 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 $3;
delete $5; 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; 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; delete $4;
$$ = new SetExpression(MakeIndexer(ScopeThis, *$2), OpSetLiteral, fexpr, @$); $$ = new SetExpression(MakeIndexer(ScopeThis, *$2), OpSetLiteral, fexpr, @$);
@ -562,11 +652,15 @@ lterm: T_LIBRARY rterm
BindToScope(expr, ScopeLocal); BindToScope(expr, ScopeLocal);
$$ = new SetExpression(expr, $3, $4, @$); $$ = 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 | T_THROW rterm
{ {
@ -613,11 +707,37 @@ rterm_array: '['
} }
rterm_items ']' rterm_items ']'
{ {
context->m_OpenBraces--;
$$ = new ArrayExpression(*$3, @$); $$ = new ArrayExpression(*$3, @$);
delete $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<Expression *> dlist;
typedef std::pair<Expression *, EItemInfo> 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: '{' rterm_scope_require_side_effect: '{'
{ {
context->m_IgnoreNewlines.push(false); context->m_IgnoreNewlines.push(false);
@ -638,6 +758,7 @@ rterm_scope_require_side_effect: '{'
} }
delete $3; delete $3;
$$ = new DictExpression(dlist, @$); $$ = new DictExpression(dlist, @$);
$$->MakeInline();
} }
; ;
@ -661,13 +782,12 @@ rterm_scope: '{'
} }
delete $3; delete $3;
$$ = new DictExpression(dlist, @$); $$ = new DictExpression(dlist, @$);
$$->MakeInline();
} }
; ;
else_if_branch: T_ELSE T_IF '(' rterm ')' rterm_scope else_if_branch: T_ELSE T_IF '(' rterm ')' rterm_scope
{ {
$6->MakeInline();
$$ = new std::pair<Expression *, Expression *>($4, $6); $$ = new std::pair<Expression *, Expression *>($4, $6);
} }
; ;
@ -691,8 +811,6 @@ rterm_side_effect: rterm '(' rterm_items ')'
} }
| T_IF '(' rterm ')' rterm_scope else_if_branches | T_IF '(' rterm ')' rterm_scope else_if_branches
{ {
$5->MakeInline();
std::vector<std::pair<Expression *, Expression *> > ebranches = *$6; std::vector<std::pair<Expression *, Expression *> > ebranches = *$6;
delete $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 | T_IF '(' rterm ')' rterm_scope else_if_branches T_ELSE rterm_scope
{ {
$5->MakeInline();
std::vector<std::pair<Expression *, Expression *> > ebranches = *$6; std::vector<std::pair<Expression *, Expression *> > ebranches = *$6;
delete $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); $$ = MakeLiteral(*$1);
delete $1; delete $1;
@ -792,11 +908,23 @@ rterm_no_side_effect: T_STRING
{ {
$$ = MakeLiteral(@$.FirstLine); $$ = MakeLiteral(@$.FirstLine);
} }
| identifier T_FOLLOWS rterm | identifier T_FOLLOWS
{ {
DictExpression *aexpr = dynamic_cast<DictExpression *>($3); BeginFlowControlBlock(context, FlowControlReturn, false);
if (aexpr) }
aexpr->MakeInline(); rterm_scope %dprec 2
{
EndFlowControlBlock(context);
std::vector<String> args;
args.push_back(*$1);
delete $1;
$$ = new FunctionExpression(args, new std::map<String, Expression *>(), $4, @$);
}
| identifier T_FOLLOWS rterm %dprec 1
{
ASSERT(!dynamic_cast<DictExpression *>($3));
std::vector<String> args; std::vector<String> args;
args.push_back(*$1); args.push_back(*$1);
@ -804,22 +932,25 @@ rterm_no_side_effect: T_STRING
$$ = new FunctionExpression(args, new std::map<String, Expression *>(), $3, @$); $$ = new FunctionExpression(args, new std::map<String, Expression *>(), $3, @$);
} }
| '(' identifier_items ')' T_FOLLOWS rterm | '(' identifier_items ')' T_FOLLOWS
{ {
DictExpression *aexpr = dynamic_cast<DictExpression *>($5); BeginFlowControlBlock(context, FlowControlReturn, false);
if (aexpr) }
aexpr->MakeInline(); rterm_scope %dprec 2
{
EndFlowControlBlock(context);
$$ = new FunctionExpression(*$2, new std::map<String, Expression *>(), $6, @$);
delete $2;
}
| '(' identifier_items ')' T_FOLLOWS rterm %dprec 1
{
ASSERT(!dynamic_cast<DictExpression *>($5));
$$ = new FunctionExpression(*$2, new std::map<String, Expression *>(), $5, @$); $$ = new FunctionExpression(*$2, new std::map<String, Expression *>(), $5, @$);
delete $2; delete $2;
} }
| rterm_array | rterm_array
| rterm_scope_require_side_effect
{
Expression *expr = $1;
BindToScope(expr, ScopeThis);
$$ = expr;
}
| '(' | '('
{ {
context->m_OpenBraces++; context->m_OpenBraces++;
@ -849,25 +980,35 @@ rterm_no_side_effect: T_STRING
| rterm T_DIVIDE_OP rterm { MakeRBinaryOp<DivideExpression>(&$$, $1, $3, @1, @3); } | rterm T_DIVIDE_OP rterm { MakeRBinaryOp<DivideExpression>(&$$, $1, $3, @1, @3); }
| rterm T_MODULO rterm { MakeRBinaryOp<ModuloExpression>(&$$, $1, $3, @1, @3); } | rterm T_MODULO rterm { MakeRBinaryOp<ModuloExpression>(&$$, $1, $3, @1, @3); }
| rterm T_XOR rterm { MakeRBinaryOp<XorExpression>(&$$, $1, $3, @1, @3); } | rterm T_XOR rterm { MakeRBinaryOp<XorExpression>(&$$, $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; 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<Expression *> dlist; std::vector<Expression *> dlist;
typedef std::pair<Expression *, EItemInfo> EListItem; typedef std::pair<Expression *, EItemInfo> EListItem;
int num = 0; int num = 0;
BOOST_FOREACH(const EListItem& litem, *$2) { BOOST_FOREACH(const EListItem& litem, *$3) {
if (!litem.second.SideEffect && num != $2->size() - 1) if (!litem.second.SideEffect && num != $3->size() - 1)
yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used."); yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
dlist.push_back(litem.first); dlist.push_back(litem.first);
num++; num++;
} }
delete $2; delete $3;
DictExpression *aexpr = new DictExpression(dlist, @$); DictExpression *aexpr = new DictExpression(dlist, @$);
aexpr->MakeInline(); 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 */ target_type_specifier: /* empty */
@ -975,8 +1127,14 @@ apply:
context->m_FVVar.push(""); context->m_FVVar.push("");
context->m_FTerm.push(NULL); 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(); context->m_Apply.pop();
String type = *$3; String type = *$3;
@ -1008,8 +1166,6 @@ apply:
BOOST_THROW_EXCEPTION(ScriptError("'apply' target type '" + target + "' is invalid", DebugInfoRange(@2, @5))); BOOST_THROW_EXCEPTION(ScriptError("'apply' target type '" + target + "' is invalid", DebugInfoRange(@2, @5)));
} }
$9->MakeInline();
bool seen_assign = context->m_SeenAssign.top(); bool seen_assign = context->m_SeenAssign.top();
context->m_SeenAssign.pop(); context->m_SeenAssign.pop();
@ -1047,7 +1203,7 @@ apply:
Expression *fterm = context->m_FTerm.top(); Expression *fterm = context->m_FTerm.top();
context->m_FTerm.pop(); 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));
} }
; ;

View File

@ -64,6 +64,13 @@ struct EItemInfo
CompilerDebugInfo DebugInfo; CompilerDebugInfo DebugInfo;
}; };
enum FlowControlType
{
FlowControlReturn = 1,
FlowControlContinue = 2,
FlowControlBreak = 4
};
struct ZoneFragment struct ZoneFragment
{ {
String Tag; String Tag;
@ -156,6 +163,7 @@ public:
std::stack<String> m_FKVar; std::stack<String> m_FKVar;
std::stack<String> m_FVVar; std::stack<String> m_FVVar;
std::stack<Expression *> m_FTerm; std::stack<Expression *> m_FTerm;
std::stack<int> m_FlowControlInfo;
}; };
} }