]> granicus.if.org Git - clang/commitdiff
C++1y: Add a step limit to constexpr evaluation, to catch runaway loops.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 8 May 2013 02:12:03 +0000 (02:12 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 8 May 2013 02:12:03 +0000 (02:12 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181388 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticASTKinds.td
include/clang/Basic/LangOptions.def
include/clang/Driver/CC1Options.td
include/clang/Driver/Options.td
lib/AST/ExprConstant.cpp
lib/Driver/Tools.cpp
lib/Frontend/CompilerInvocation.cpp

index c69f85f18d3d7a4ee455ed7577db2f1aa54a38cf..06c297e550a48690ad14ae10be1177c6aadf6218 100644 (file)
@@ -83,6 +83,8 @@ def note_constexpr_depth_limit_exceeded : Note<
   "constexpr evaluation exceeded maximum depth of %0 calls">;
 def note_constexpr_call_limit_exceeded : Note<
   "constexpr evaluation hit maximum call limit">;
+def note_constexpr_step_limit_exceeded : Note<
+  "constexpr evaluation hit maximum step limit; possible infinite loop?">;
 def note_constexpr_lifetime_ended : Note<
   "%select{read of|assignment to|increment of|decrement of}0 "
   "%select{temporary|variable}1 whose lifetime has ended">;
index b17dfbc9b520872055ef8f759e9dc9e7155fedcf..e8fe482fa76df49916598bc9a11491da12a435e7 100644 (file)
@@ -160,6 +160,8 @@ BENIGN_LANGOPT(InstantiationDepth, 32, 256,
                "maximum template instantiation depth")
 BENIGN_LANGOPT(ConstexprCallDepth, 32, 512,
                "maximum constexpr call depth")
+BENIGN_LANGOPT(ConstexprStepLimit, 32, 1048576,
+               "maximum constexpr evaluation steps")
 BENIGN_LANGOPT(BracketDepth, 32, 256,
                "maximum bracket nesting depth")
 BENIGN_LANGOPT(NumLargeByValueCopy, 32, 0, 
index 96a50fc5c66a0b65509ec0f42fa7f10abb95e263..afe65c47a8e239b6e97e19ee6514e17817ed94b1 100644 (file)
@@ -434,6 +434,8 @@ def ftemplate_depth : Separate<["-"], "ftemplate-depth">,
   HelpText<"Maximum depth of recursive template instantiation">;
 def fconstexpr_depth : Separate<["-"], "fconstexpr-depth">,
   HelpText<"Maximum depth of recursive constexpr function calls">;
+def fconstexpr_steps : Separate<["-"], "fconstexpr-steps">,
+  HelpText<"Maximum number of steps in constexpr function evaluation">;
 def fbracket_depth : Separate<["-"], "fbracket-depth">,
   HelpText<"Maximum nesting level for parentheses, brackets, and braces">;
 def fconst_strings : Flag<["-"], "fconst-strings">,
index 3a5358a7e35535f73639d08d619aa371ba5bf375..0345f40137dafc7645985468c82f7de17138c58c 100644 (file)
@@ -344,6 +344,7 @@ def fcompile_resource_EQ : Joined<["-"], "fcompile-resource=">, Group<f_Group>;
 def fconstant_cfstrings : Flag<["-"], "fconstant-cfstrings">, Group<f_Group>;
 def fconstant_string_class_EQ : Joined<["-"], "fconstant-string-class=">, Group<f_Group>;
 def fconstexpr_depth_EQ : Joined<["-"], "fconstexpr-depth=">, Group<f_Group>;
+def fconstexpr_steps_EQ : Joined<["-"], "fconstexpr-steps=">, Group<f_Group>;
 def fconstexpr_backtrace_limit_EQ : Joined<["-"], "fconstexpr-backtrace-limit=">,
                                     Group<f_Group>;
 def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, Group<f_clang_Group>, Flags<[NoArgumentUnused]>;
index 1460267e8bc83e3e27449ec247f97eefb7180e1f..08c26a3ce30882bc5ff259aa16f250aff46e7900 100644 (file)
@@ -380,6 +380,11 @@ namespace {
     /// NextCallIndex - The next call index to assign.
     unsigned NextCallIndex;
 
+    /// StepsLeft - The remaining number of evaluation steps we're permitted
+    /// to perform. This is essentially a limit for the number of statements
+    /// we will evaluate.
+    unsigned StepsLeft;
+
     /// BottomFrame - The frame in which evaluation started. This must be
     /// initialized after CurrentCall and CallStackDepth.
     CallStackFrame BottomFrame;
@@ -404,9 +409,10 @@ namespace {
     bool IntOverflowCheckMode;
 
     EvalInfo(const ASTContext &C, Expr::EvalStatus &S,
-             bool OverflowCheckMode=false)
+             bool OverflowCheckMode = false)
       : Ctx(const_cast<ASTContext&>(C)), EvalStatus(S), CurrentCall(0),
         CallStackDepth(0), NextCallIndex(1),
+        StepsLeft(getLangOpts().ConstexprStepLimit),
         BottomFrame(*this, SourceLocation(), 0, 0, 0),
         EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false),
         CheckingPotentialConstantExpression(false),
@@ -446,6 +452,15 @@ namespace {
       return (Frame->Index == CallIndex) ? Frame : 0;
     }
 
+    bool nextStep(const Stmt *S) {
+      if (!StepsLeft) {
+        Diag(S->getLocStart(), diag::note_constexpr_step_limit_exceeded);
+        return false;
+      }
+      --StepsLeft;
+      return true;
+    }
+
   private:
     /// Add a diagnostic to the diagnostics list.
     PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) {
@@ -530,9 +545,9 @@ namespace {
     bool keepEvaluatingAfterFailure() {
       // Should return true in IntOverflowCheckMode, so that we check for
       // overflow even if some subexpressions can't be evaluated as constants.
-      return IntOverflowCheckMode ||
-             (CheckingPotentialConstantExpression &&
-              EvalStatus.Diag && EvalStatus.Diag->empty());
+      return StepsLeft && (IntOverflowCheckMode ||
+                           (CheckingPotentialConstantExpression &&
+                            EvalStatus.Diag && EvalStatus.Diag->empty()));
     }
   };
 
@@ -2794,6 +2809,9 @@ static EvalStmtResult EvaluateLoopBody(APValue &Result, EvalInfo &Info,
 // Evaluate a statement.
 static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info,
                                    const Stmt *S) {
+  if (!Info.nextStep(S))
+    return ESR_Failed;
+
   // FIXME: Mark all temporaries in the current frame as destroyed at
   // the end of each full-expression.
   switch (S->getStmtClass()) {
index 47d12e1e3b8a466692561736ecb76fa5b0770af5..a86199ef9c52749e6c033e24f06aa1bf5c4834f3 100644 (file)
@@ -2667,6 +2667,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back(A->getValue());
   }
 
+  if (Arg *A = Args.getLastArg(options::OPT_fconstexpr_steps_EQ)) {
+    CmdArgs.push_back("-fconstexpr-steps");
+    CmdArgs.push_back(A->getValue());
+  }
+
   if (Arg *A = Args.getLastArg(options::OPT_fbracket_depth_EQ)) {
     CmdArgs.push_back("-fbracket-depth");
     CmdArgs.push_back(A->getValue());
index 42ea96f0f2add21b70c8f95599664f80e6b7b9d6..9c8eea10ad72ecee0c225e4d7a48735d39b7ce01 100644 (file)
@@ -1251,6 +1251,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
                                                     Diags);
   Opts.ConstexprCallDepth = Args.getLastArgIntValue(OPT_fconstexpr_depth, 512,
                                                     Diags);
+  Opts.ConstexprStepLimit = Args.getLastArgIntValue(OPT_fconstexpr_steps,
+                                                    1048576, Diags);
   Opts.BracketDepth = Args.getLastArgIntValue(OPT_fbracket_depth, 256, Diags);
   Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing);
   Opts.NumLargeByValueCopy = Args.getLastArgIntValue(OPT_Wlarge_by_value_copy_EQ,