Author: Timm Bäder Date: 2023-05-31T12:08:03+02:00 New Revision: da836b36bc3540d21c947a95474d2bb6cc458951
URL: https://github.com/llvm/llvm-project/commit/da836b36bc3540d21c947a95474d2bb6cc458951 DIFF: https://github.com/llvm/llvm-project/commit/da836b36bc3540d21c947a95474d2bb6cc458951.diff LOG: [clang][Interp] Track frame depth Save the depth of each InterpFrame and bail out if we're too deep. Differential Revision: https://reviews.llvm.org/D148614 Added: clang/test/AST/Interp/depth-limit.cpp clang/test/AST/Interp/depth-limit2.cpp Modified: clang/lib/AST/Interp/Interp.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/InterpFrame.cpp clang/lib/AST/Interp/InterpFrame.h clang/lib/AST/Interp/InterpState.cpp clang/lib/AST/Interp/InterpState.h Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index 0479f4c60c16..3798146b32d1 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -341,6 +341,17 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { return true; } +bool CheckCallDepth(InterpState &S, CodePtr OpPC) { + if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_depth_limit_exceeded) + << S.getLangOpts().ConstexprCallDepth; + return false; + } + + return true; +} + bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) { if (!This.isZero()) return true; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 64bdd872221a..fd5ce3c32596 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -88,6 +88,10 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a method can be called. bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F); +/// Checks if calling the currently active function would exceed +/// the allowed call depth. +bool CheckCallDepth(InterpState &S, CodePtr OpPC); + /// Checks the 'this' pointer. bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); @@ -158,7 +162,6 @@ enum class ArithOp { Add, Sub }; template <PrimType Name, bool Builtin = false, class T = typename PrimConv<Name>::T> bool Ret(InterpState &S, CodePtr &PC, APValue &Result) { - S.CallStackDepth--; const T &Ret = S.Stk.pop<T>(); assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); @@ -181,8 +184,6 @@ bool Ret(InterpState &S, CodePtr &PC, APValue &Result) { template <bool Builtin = false> inline bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) { - S.CallStackDepth--; - assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); if (Builtin || !S.checkingPotentialConstantExpression()) S.Current->popArgs(); @@ -1598,6 +1599,9 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) { if (!CheckCallable(S, OpPC, Func)) return false; + if (!CheckCallDepth(S, OpPC)) + return false; + auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC); InterpFrame *FrameBefore = S.Current; S.Current = NewFrame.get(); diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp index e20f283c2855..14b55bea8820 100644 --- a/clang/lib/AST/Interp/InterpFrame.cpp +++ b/clang/lib/AST/Interp/InterpFrame.cpp @@ -23,8 +23,8 @@ using namespace clang::interp; InterpFrame::InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller, CodePtr RetPC) - : Caller(Caller), S(S), Func(Func), RetPC(RetPC), - ArgSize(Func ? Func->getArgSize() : 0), + : Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func), + RetPC(RetPC), ArgSize(Func ? Func->getArgSize() : 0), Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) { if (!Func) return; diff --git a/clang/lib/AST/Interp/InterpFrame.h b/clang/lib/AST/Interp/InterpFrame.h index c0f4825096be..7988e74a61fe 100644 --- a/clang/lib/AST/Interp/InterpFrame.h +++ b/clang/lib/AST/Interp/InterpFrame.h @@ -15,7 +15,6 @@ #include "Frame.h" #include "Program.h" -#include "State.h" #include <cstdint> #include <vector> @@ -120,6 +119,8 @@ class InterpFrame final : public Frame { const Expr *getExpr(CodePtr PC) const; SourceLocation getLocation(CodePtr PC) const; + unsigned getDepth() const { return Depth; } + private: /// Returns an original argument from the stack. template <typename T> const T &stackRef(unsigned Offset) const { @@ -145,6 +146,8 @@ class InterpFrame final : public Frame { private: /// Reference to the interpreter state. InterpState &S; + /// Depth of this frame. + unsigned Depth; /// Reference to the function being executed. const Function *Func; /// Current object pointer for methods. diff --git a/clang/lib/AST/Interp/InterpState.cpp b/clang/lib/AST/Interp/InterpState.cpp index 6ae4ecd78c0f..bd7daf38796c 100644 --- a/clang/lib/AST/Interp/InterpState.cpp +++ b/clang/lib/AST/Interp/InterpState.cpp @@ -17,8 +17,7 @@ using namespace clang::interp; InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, SourceMapper *M) - : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr), - CallStackDepth(Parent.getCallStackDepth() + 1) {} + : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr) {} InterpState::~InterpState() { while (Current) { diff --git a/clang/lib/AST/Interp/InterpState.h b/clang/lib/AST/Interp/InterpState.h index 033080637385..74c4667bb019 100644 --- a/clang/lib/AST/Interp/InterpState.h +++ b/clang/lib/AST/Interp/InterpState.h @@ -15,6 +15,7 @@ #include "Context.h" #include "Function.h" +#include "InterpFrame.h" #include "InterpStack.h" #include "State.h" #include "clang/AST/APValue.h" @@ -41,7 +42,9 @@ class InterpState final : public State, public SourceMapper { // Stack frame accessors. Frame *getSplitFrame() { return Parent.getCurrentFrame(); } Frame *getCurrentFrame() override; - unsigned getCallStackDepth() override { return CallStackDepth; } + unsigned getCallStackDepth() override { + return Current ? (Current->getDepth() + 1) : 1; + } const Frame *getBottomFrame() const override { return Parent.getBottomFrame(); } @@ -103,8 +106,6 @@ class InterpState final : public State, public SourceMapper { Context &Ctx; /// The current frame. InterpFrame *Current = nullptr; - /// Call stack depth. - unsigned CallStackDepth; }; } // namespace interp diff --git a/clang/test/AST/Interp/depth-limit.cpp b/clang/test/AST/Interp/depth-limit.cpp new file mode 100644 index 000000000000..3e8a29c569ce --- /dev/null +++ b/clang/test/AST/Interp/depth-limit.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fconstexpr-depth 100 -verify %s +// RUN: %clang_cc1 -fconstexpr-depth 100 -verify=ref %s + +constexpr int f(int a) { + if (a == 100) + return 1 / 0; // expected-warning {{division by zero is undefined}} \ + // ref-warning {{division by zero is undefined}} + + return f(a + 1); // ref-note {{exceeded maximum depth of 100 calls}} \ + // ref-note {{in call to 'f(99)'}} \ + // ref-note {{in call to 'f(98)'}} \ + // ref-note {{in call to 'f(97)'}} \ + // ref-note {{in call to 'f(96)'}} \ + // ref-note {{in call to 'f(95)'}} \ + // ref-note {{skipping 90 calls in backtrace}} \ + // ref-note {{in call to 'f(4)'}} \ + // ref-note {{in call to 'f(3)'}} \ + // ref-note {{in call to 'f(2)'}} \ + // ref-note {{in call to 'f(1)'}} \ + // expected-note {{exceeded maximum depth of 100 calls}} \ + // expected-note {{in call to 'f(99)'}} \ + // expected-note {{in call to 'f(98)'}} \ + // expected-note {{in call to 'f(97)'}} \ + // expected-note {{in call to 'f(96)'}} \ + // expected-note {{in call to 'f(95)'}} \ + // expected-note {{skipping 90 calls in backtrace}} \ + // expected-note {{in call to 'f(4)'}} \ + // expected-note {{in call to 'f(3)'}} \ + // expected-note {{in call to 'f(2)'}} \ + // expected-note {{in call to 'f(1)'}} +} +static_assert(f(0) == 100); // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'f(0)'}} \ + // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to 'f(0)'}} diff --git a/clang/test/AST/Interp/depth-limit2.cpp b/clang/test/AST/Interp/depth-limit2.cpp new file mode 100644 index 000000000000..614472c68ba9 --- /dev/null +++ b/clang/test/AST/Interp/depth-limit2.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fconstexpr-depth 2 -verify %s +// RUN: %clang_cc1 -fconstexpr-depth 2 -verify=ref %s + + +constexpr int func() { + return 12; +} + +constexpr int foo() { + return func(); // expected-note {{exceeded maximum depth of 2 calls}} \ + // ref-note {{exceeded maximum depth of 2 calls}} +} + +constexpr int bar() { + return foo(); // expected-note {{in call to 'foo()'}} \ + // ref-note {{in call to 'foo()'}} +} + +static_assert(bar() == 12); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to 'bar()'}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'bar()'}} + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits