Skip to content

Commit da836b3

Browse files
committed
[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
1 parent a2e3b6f commit da836b3

File tree

8 files changed

+87
-11
lines changed

8 files changed

+87
-11
lines changed

clang/lib/AST/Interp/Interp.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,17 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
341341
return true;
342342
}
343343

344+
bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
345+
if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) {
346+
S.FFDiag(S.Current->getSource(OpPC),
347+
diag::note_constexpr_depth_limit_exceeded)
348+
<< S.getLangOpts().ConstexprCallDepth;
349+
return false;
350+
}
351+
352+
return true;
353+
}
354+
344355
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
345356
if (!This.isZero())
346357
return true;

clang/lib/AST/Interp/Interp.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
8888
/// Checks if a method can be called.
8989
bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
9090

91+
/// Checks if calling the currently active function would exceed
92+
/// the allowed call depth.
93+
bool CheckCallDepth(InterpState &S, CodePtr OpPC);
94+
9195
/// Checks the 'this' pointer.
9296
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
9397

@@ -158,7 +162,6 @@ enum class ArithOp { Add, Sub };
158162
template <PrimType Name, bool Builtin = false,
159163
class T = typename PrimConv<Name>::T>
160164
bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
161-
S.CallStackDepth--;
162165
const T &Ret = S.Stk.pop<T>();
163166

164167
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
@@ -181,8 +184,6 @@ bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
181184

182185
template <bool Builtin = false>
183186
inline bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
184-
S.CallStackDepth--;
185-
186187
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
187188
if (Builtin || !S.checkingPotentialConstantExpression())
188189
S.Current->popArgs();
@@ -1598,6 +1599,9 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
15981599
if (!CheckCallable(S, OpPC, Func))
15991600
return false;
16001601

1602+
if (!CheckCallDepth(S, OpPC))
1603+
return false;
1604+
16011605
auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC);
16021606
InterpFrame *FrameBefore = S.Current;
16031607
S.Current = NewFrame.get();

clang/lib/AST/Interp/InterpFrame.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ using namespace clang::interp;
2323

2424
InterpFrame::InterpFrame(InterpState &S, const Function *Func,
2525
InterpFrame *Caller, CodePtr RetPC)
26-
: Caller(Caller), S(S), Func(Func), RetPC(RetPC),
27-
ArgSize(Func ? Func->getArgSize() : 0),
26+
: Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func),
27+
RetPC(RetPC), ArgSize(Func ? Func->getArgSize() : 0),
2828
Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
2929
if (!Func)
3030
return;

clang/lib/AST/Interp/InterpFrame.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
#include "Frame.h"
1717
#include "Program.h"
18-
#include "State.h"
1918
#include <cstdint>
2019
#include <vector>
2120

@@ -120,6 +119,8 @@ class InterpFrame final : public Frame {
120119
const Expr *getExpr(CodePtr PC) const;
121120
SourceLocation getLocation(CodePtr PC) const;
122121

122+
unsigned getDepth() const { return Depth; }
123+
123124
private:
124125
/// Returns an original argument from the stack.
125126
template <typename T> const T &stackRef(unsigned Offset) const {
@@ -145,6 +146,8 @@ class InterpFrame final : public Frame {
145146
private:
146147
/// Reference to the interpreter state.
147148
InterpState &S;
149+
/// Depth of this frame.
150+
unsigned Depth;
148151
/// Reference to the function being executed.
149152
const Function *Func;
150153
/// Current object pointer for methods.

clang/lib/AST/Interp/InterpState.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ using namespace clang::interp;
1717

1818
InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
1919
Context &Ctx, SourceMapper *M)
20-
: Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr),
21-
CallStackDepth(Parent.getCallStackDepth() + 1) {}
20+
: Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr) {}
2221

2322
InterpState::~InterpState() {
2423
while (Current) {

clang/lib/AST/Interp/InterpState.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "Context.h"
1717
#include "Function.h"
18+
#include "InterpFrame.h"
1819
#include "InterpStack.h"
1920
#include "State.h"
2021
#include "clang/AST/APValue.h"
@@ -41,7 +42,9 @@ class InterpState final : public State, public SourceMapper {
4142
// Stack frame accessors.
4243
Frame *getSplitFrame() { return Parent.getCurrentFrame(); }
4344
Frame *getCurrentFrame() override;
44-
unsigned getCallStackDepth() override { return CallStackDepth; }
45+
unsigned getCallStackDepth() override {
46+
return Current ? (Current->getDepth() + 1) : 1;
47+
}
4548
const Frame *getBottomFrame() const override {
4649
return Parent.getBottomFrame();
4750
}
@@ -103,8 +106,6 @@ class InterpState final : public State, public SourceMapper {
103106
Context &Ctx;
104107
/// The current frame.
105108
InterpFrame *Current = nullptr;
106-
/// Call stack depth.
107-
unsigned CallStackDepth;
108109
};
109110

110111
} // namespace interp

clang/test/AST/Interp/depth-limit.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fconstexpr-depth 100 -verify %s
2+
// RUN: %clang_cc1 -fconstexpr-depth 100 -verify=ref %s
3+
4+
constexpr int f(int a) {
5+
if (a == 100)
6+
return 1 / 0; // expected-warning {{division by zero is undefined}} \
7+
// ref-warning {{division by zero is undefined}}
8+
9+
return f(a + 1); // ref-note {{exceeded maximum depth of 100 calls}} \
10+
// ref-note {{in call to 'f(99)'}} \
11+
// ref-note {{in call to 'f(98)'}} \
12+
// ref-note {{in call to 'f(97)'}} \
13+
// ref-note {{in call to 'f(96)'}} \
14+
// ref-note {{in call to 'f(95)'}} \
15+
// ref-note {{skipping 90 calls in backtrace}} \
16+
// ref-note {{in call to 'f(4)'}} \
17+
// ref-note {{in call to 'f(3)'}} \
18+
// ref-note {{in call to 'f(2)'}} \
19+
// ref-note {{in call to 'f(1)'}} \
20+
// expected-note {{exceeded maximum depth of 100 calls}} \
21+
// expected-note {{in call to 'f(99)'}} \
22+
// expected-note {{in call to 'f(98)'}} \
23+
// expected-note {{in call to 'f(97)'}} \
24+
// expected-note {{in call to 'f(96)'}} \
25+
// expected-note {{in call to 'f(95)'}} \
26+
// expected-note {{skipping 90 calls in backtrace}} \
27+
// expected-note {{in call to 'f(4)'}} \
28+
// expected-note {{in call to 'f(3)'}} \
29+
// expected-note {{in call to 'f(2)'}} \
30+
// expected-note {{in call to 'f(1)'}}
31+
}
32+
static_assert(f(0) == 100); // ref-error {{not an integral constant expression}} \
33+
// ref-note {{in call to 'f(0)'}} \
34+
// expected-error {{not an integral constant expression}} \
35+
// expected-note {{in call to 'f(0)'}}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fconstexpr-depth 2 -verify %s
2+
// RUN: %clang_cc1 -fconstexpr-depth 2 -verify=ref %s
3+
4+
5+
constexpr int func() {
6+
return 12;
7+
}
8+
9+
constexpr int foo() {
10+
return func(); // expected-note {{exceeded maximum depth of 2 calls}} \
11+
// ref-note {{exceeded maximum depth of 2 calls}}
12+
}
13+
14+
constexpr int bar() {
15+
return foo(); // expected-note {{in call to 'foo()'}} \
16+
// ref-note {{in call to 'foo()'}}
17+
}
18+
19+
static_assert(bar() == 12); // expected-error {{not an integral constant expression}} \
20+
// expected-note {{in call to 'bar()'}} \
21+
// ref-error {{not an integral constant expression}} \
22+
// ref-note {{in call to 'bar()'}}
23+

0 commit comments

Comments
 (0)