Author: Timm Baeder
Date: 2025-04-29T05:47:22+02:00
New Revision: 15579a8e72589b4fdf45c8f5bca52e58dcc9ce1d

URL: 
https://github.com/llvm/llvm-project/commit/15579a8e72589b4fdf45c8f5bca52e58dcc9ce1d
DIFF: 
https://github.com/llvm/llvm-project/commit/15579a8e72589b4fdf45c8f5bca52e58dcc9ce1d.diff

LOG: [clang][bytecode] Check array sizes against step limit (#137679)

Added: 
    clang/test/AST/ByteCode/dynalloc-limits.cpp

Modified: 
    clang/lib/AST/ByteCode/Compiler.cpp
    clang/lib/AST/ByteCode/Interp.h
    clang/lib/AST/ByteCode/InterpBuiltin.cpp
    clang/lib/AST/ByteCode/Opcodes.td

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 9a1e61b54b8be..fe8d05c001a31 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -1862,6 +1862,13 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const 
Expr *> Inits,
     if (Inits.size() == 1 && QT == Inits[0]->getType())
       return this->delegate(Inits[0]);
 
+    const ConstantArrayType *CAT =
+        Ctx.getASTContext().getAsConstantArrayType(QT);
+    uint64_t NumElems = CAT->getZExtSize();
+
+    if (!this->emitCheckArraySize(NumElems, E))
+      return false;
+
     unsigned ElementIndex = 0;
     for (const Expr *Init : Inits) {
       if (const auto *EmbedS =
@@ -1890,10 +1897,6 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const 
Expr *> Inits,
     // Expand the filler expression.
     // FIXME: This should go away.
     if (ArrayFiller) {
-      const ConstantArrayType *CAT =
-          Ctx.getASTContext().getAsConstantArrayType(QT);
-      uint64_t NumElems = CAT->getZExtSize();
-
       for (; ElementIndex != NumElems; ++ElementIndex) {
         if (!this->visitArrayElemInit(ElementIndex, ArrayFiller))
           return false;

diff  --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 6cd995279029a..80488b5fa3f46 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -175,6 +175,8 @@ bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
 
 bool isConstexprUnknown(const Pointer &P);
 
+inline bool CheckArraySize(InterpState &S, CodePtr OpPC, uint64_t NumElems);
+
 enum class ShiftDir { Left, Right };
 
 /// Checks if the shift operation is legal.
@@ -3110,6 +3112,9 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType 
T, const Expr *Source,
   }
   assert(NumElements.isPositive());
 
+  if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements)))
+    return false;
+
   DynamicAllocator &Allocator = S.getAllocator();
   Block *B =
       Allocator.allocate(Source, T, static_cast<size_t>(NumElements),
@@ -3140,6 +3145,9 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const 
Descriptor *ElementDesc,
   }
   assert(NumElements.isPositive());
 
+  if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements)))
+    return false;
+
   DynamicAllocator &Allocator = S.getAllocator();
   Block *B =
       Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements),
@@ -3246,6 +3254,17 @@ inline bool CheckDestruction(InterpState &S, CodePtr 
OpPC) {
   return CheckDestructor(S, OpPC, Ptr);
 }
 
+inline bool CheckArraySize(InterpState &S, CodePtr OpPC, uint64_t NumElems) {
+  uint64_t Limit = S.getLangOpts().ConstexprStepLimit;
+  if (NumElems > Limit) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_new_exceeds_limits)
+        << NumElems << Limit;
+    return false;
+  }
+  return true;
+}
+
 
//===----------------------------------------------------------------------===//
 // Read opcode arguments
 
//===----------------------------------------------------------------------===//

diff  --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index e3d76326db44b..0b4585a10a3d5 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1533,6 +1533,9 @@ static bool interp__builtin_operator_new(InterpState &S, 
CodePtr OpPC,
     return false;
   }
 
+  if (!CheckArraySize(S, OpPC, NumElems.getZExtValue()))
+    return false;
+
   bool IsArray = NumElems.ugt(1);
   std::optional<PrimType> ElemT = S.getContext().classify(ElemType);
   DynamicAllocator &Allocator = S.getAllocator();

diff  --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index e71790211293a..65a9a0cdad022 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -414,6 +414,8 @@ def CheckLiteralType : Opcode {
   let Args = [ArgTypePtr];
 }
 
+def CheckArraySize : Opcode { let Args = [ArgUint64]; }
+
 // [] -> [Value]
 def GetGlobal : AccessOpcode;
 def GetGlobalUnchecked : AccessOpcode;

diff  --git a/clang/test/AST/ByteCode/dynalloc-limits.cpp 
b/clang/test/AST/ByteCode/dynalloc-limits.cpp
new file mode 100644
index 0000000000000..ed2d4faf974b1
--- /dev/null
+++ b/clang/test/AST/ByteCode/dynalloc-limits.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++20 -verify=ref,both      -fconstexpr-steps=1024 
-Wvla %s
+// RUN: %clang_cc1 -std=c++20 -verify=both,both -fconstexpr-steps=1024 -Wvla 
%s -fexperimental-new-constant-interpreter
+
+
+
+
+namespace std {
+  using size_t = decltype(sizeof(0));
+}
+
+void *operator new(std::size_t, void *p) { return p; }
+
+namespace std {
+  template<typename T> struct allocator {
+    constexpr T *allocate(size_t N) {
+      return (T*)operator new(sizeof(T) * N); // #alloc
+    }
+    constexpr void deallocate(void *p) {
+      operator delete(p);
+    }
+  };
+  template<typename T, typename ...Args>
+  constexpr void construct_at(void *p, Args &&...args) { // #construct
+    new (p) T((Args&&)args...);
+  }
+}
+
+template <typename T>
+struct S {
+    constexpr S(unsigned long long N)
+    : data(nullptr){
+        data = alloc.allocate(N);  // #call
+        for(std::size_t i = 0; i < N; i ++)
+            std::construct_at<T>(data + i, i); // #construct_call
+    }
+    constexpr T operator[](std::size_t i) const {
+      return data[i];
+    }
+
+    constexpr ~S() {
+        alloc.deallocate(data);
+    }
+    std::allocator<T> alloc;
+    T* data;
+};
+
+#if __LP64__
+constexpr std::size_t s = S<std::size_t>(~0UL)[42]; // both-error {{constexpr 
variable 's' must be initialized by a constant expression}} \
+                                           // both-note-re@#call {{in call to 
'this->alloc.allocate({{.*}})'}} \
+                                           // both-note-re@#alloc {{cannot 
allocate array; evaluated array bound {{.*}} is too large}} \
+                                           // both-note-re {{in call to 
'S({{.*}})'}}
+#endif
+
+constexpr std::size_t ssmall = S<std::size_t>(100)[42];
+
+constexpr std::size_t s5 = S<std::size_t>(1025)[42]; // both-error {{constexpr 
variable 's5' must be initialized by a constant expression}} \
+                                   // both-note@#alloc {{cannot allocate 
array; evaluated array bound 1025 exceeds the limit (1024); use 
'-fconstexpr-steps' to increase this limit}} \
+                                   // both-note@#call {{in call to 
'this->alloc.allocate(1025)'}} \
+                                   // both-note {{in call}}
+
+
+
+template <auto N>
+constexpr int stack_array() {
+    [[maybe_unused]] char BIG[N] = {1};  // both-note {{cannot allocate array; 
evaluated array bound 1025 exceeds the limit (1024)}}
+    return BIG[N-1];
+}
+
+int c = stack_array<1024>();
+int d = stack_array<1025>();
+constexpr int e = stack_array<1024>();
+constexpr int f = stack_array<1025>(); // both-error {{constexpr variable 'f' 
must be initialized by a constant expression}} \
+                                       // both-note {{in call}}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to