Skip to content

Commit 15579a8

Browse files
authored
[clang][bytecode] Check array sizes against step limit (#137679)
1 parent 7ce0f5a commit 15579a8

File tree

5 files changed

+104
-4
lines changed

5 files changed

+104
-4
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,6 +1862,13 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
18621862
if (Inits.size() == 1 && QT == Inits[0]->getType())
18631863
return this->delegate(Inits[0]);
18641864

1865+
const ConstantArrayType *CAT =
1866+
Ctx.getASTContext().getAsConstantArrayType(QT);
1867+
uint64_t NumElems = CAT->getZExtSize();
1868+
1869+
if (!this->emitCheckArraySize(NumElems, E))
1870+
return false;
1871+
18651872
unsigned ElementIndex = 0;
18661873
for (const Expr *Init : Inits) {
18671874
if (const auto *EmbedS =
@@ -1890,10 +1897,6 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
18901897
// Expand the filler expression.
18911898
// FIXME: This should go away.
18921899
if (ArrayFiller) {
1893-
const ConstantArrayType *CAT =
1894-
Ctx.getASTContext().getAsConstantArrayType(QT);
1895-
uint64_t NumElems = CAT->getZExtSize();
1896-
18971900
for (; ElementIndex != NumElems; ++ElementIndex) {
18981901
if (!this->visitArrayElemInit(ElementIndex, ArrayFiller))
18991902
return false;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
175175

176176
bool isConstexprUnknown(const Pointer &P);
177177

178+
inline bool CheckArraySize(InterpState &S, CodePtr OpPC, uint64_t NumElems);
179+
178180
enum class ShiftDir { Left, Right };
179181

180182
/// Checks if the shift operation is legal.
@@ -3110,6 +3112,9 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
31103112
}
31113113
assert(NumElements.isPositive());
31123114

3115+
if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements)))
3116+
return false;
3117+
31133118
DynamicAllocator &Allocator = S.getAllocator();
31143119
Block *B =
31153120
Allocator.allocate(Source, T, static_cast<size_t>(NumElements),
@@ -3140,6 +3145,9 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
31403145
}
31413146
assert(NumElements.isPositive());
31423147

3148+
if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements)))
3149+
return false;
3150+
31433151
DynamicAllocator &Allocator = S.getAllocator();
31443152
Block *B =
31453153
Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements),
@@ -3246,6 +3254,17 @@ inline bool CheckDestruction(InterpState &S, CodePtr OpPC) {
32463254
return CheckDestructor(S, OpPC, Ptr);
32473255
}
32483256

3257+
inline bool CheckArraySize(InterpState &S, CodePtr OpPC, uint64_t NumElems) {
3258+
uint64_t Limit = S.getLangOpts().ConstexprStepLimit;
3259+
if (NumElems > Limit) {
3260+
S.FFDiag(S.Current->getSource(OpPC),
3261+
diag::note_constexpr_new_exceeds_limits)
3262+
<< NumElems << Limit;
3263+
return false;
3264+
}
3265+
return true;
3266+
}
3267+
32493268
//===----------------------------------------------------------------------===//
32503269
// Read opcode arguments
32513270
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,9 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
15331533
return false;
15341534
}
15351535

1536+
if (!CheckArraySize(S, OpPC, NumElems.getZExtValue()))
1537+
return false;
1538+
15361539
bool IsArray = NumElems.ugt(1);
15371540
std::optional<PrimType> ElemT = S.getContext().classify(ElemType);
15381541
DynamicAllocator &Allocator = S.getAllocator();

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ def CheckLiteralType : Opcode {
414414
let Args = [ArgTypePtr];
415415
}
416416

417+
def CheckArraySize : Opcode { let Args = [ArgUint64]; }
418+
417419
// [] -> [Value]
418420
def GetGlobal : AccessOpcode;
419421
def GetGlobalUnchecked : AccessOpcode;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %clang_cc1 -std=c++20 -verify=ref,both -fconstexpr-steps=1024 -Wvla %s
2+
// RUN: %clang_cc1 -std=c++20 -verify=both,both -fconstexpr-steps=1024 -Wvla %s -fexperimental-new-constant-interpreter
3+
4+
5+
6+
7+
namespace std {
8+
using size_t = decltype(sizeof(0));
9+
}
10+
11+
void *operator new(std::size_t, void *p) { return p; }
12+
13+
namespace std {
14+
template<typename T> struct allocator {
15+
constexpr T *allocate(size_t N) {
16+
return (T*)operator new(sizeof(T) * N); // #alloc
17+
}
18+
constexpr void deallocate(void *p) {
19+
operator delete(p);
20+
}
21+
};
22+
template<typename T, typename ...Args>
23+
constexpr void construct_at(void *p, Args &&...args) { // #construct
24+
new (p) T((Args&&)args...);
25+
}
26+
}
27+
28+
template <typename T>
29+
struct S {
30+
constexpr S(unsigned long long N)
31+
: data(nullptr){
32+
data = alloc.allocate(N); // #call
33+
for(std::size_t i = 0; i < N; i ++)
34+
std::construct_at<T>(data + i, i); // #construct_call
35+
}
36+
constexpr T operator[](std::size_t i) const {
37+
return data[i];
38+
}
39+
40+
constexpr ~S() {
41+
alloc.deallocate(data);
42+
}
43+
std::allocator<T> alloc;
44+
T* data;
45+
};
46+
47+
#if __LP64__
48+
constexpr std::size_t s = S<std::size_t>(~0UL)[42]; // both-error {{constexpr variable 's' must be initialized by a constant expression}} \
49+
// both-note-re@#call {{in call to 'this->alloc.allocate({{.*}})'}} \
50+
// both-note-re@#alloc {{cannot allocate array; evaluated array bound {{.*}} is too large}} \
51+
// both-note-re {{in call to 'S({{.*}})'}}
52+
#endif
53+
54+
constexpr std::size_t ssmall = S<std::size_t>(100)[42];
55+
56+
constexpr std::size_t s5 = S<std::size_t>(1025)[42]; // both-error {{constexpr variable 's5' must be initialized by a constant expression}} \
57+
// both-note@#alloc {{cannot allocate array; evaluated array bound 1025 exceeds the limit (1024); use '-fconstexpr-steps' to increase this limit}} \
58+
// both-note@#call {{in call to 'this->alloc.allocate(1025)'}} \
59+
// both-note {{in call}}
60+
61+
62+
63+
template <auto N>
64+
constexpr int stack_array() {
65+
[[maybe_unused]] char BIG[N] = {1}; // both-note {{cannot allocate array; evaluated array bound 1025 exceeds the limit (1024)}}
66+
return BIG[N-1];
67+
}
68+
69+
int c = stack_array<1024>();
70+
int d = stack_array<1025>();
71+
constexpr int e = stack_array<1024>();
72+
constexpr int f = stack_array<1025>(); // both-error {{constexpr variable 'f' must be initialized by a constant expression}} \
73+
// both-note {{in call}}

0 commit comments

Comments
 (0)