Author: Timm Baeder Date: 2025-02-16T12:24:35+01:00 New Revision: e8d848824bbd76892a7b371a8409b0fb378a55a4
URL: https://github.com/llvm/llvm-project/commit/e8d848824bbd76892a7b371a8409b0fb378a55a4 DIFF: https://github.com/llvm/llvm-project/commit/e8d848824bbd76892a7b371a8409b0fb378a55a4.diff LOG: [clang][bytecode] Fix dynamic array allocation return values (#127387) We need to return a pointer to the first element, not the array itself. Added: clang/test/AST/ByteCode/allocate-arrays.cpp Modified: clang/lib/AST/ByteCode/DynamicAllocator.cpp clang/lib/AST/ByteCode/Interp.h clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ByteCode/Program.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp index 819fbdb8b070b..3ef8c2e1f3e7c 100644 --- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp +++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp @@ -54,6 +54,7 @@ Block *DynamicAllocator::allocate(const Expr *Source, PrimType T, Block *DynamicAllocator::allocate(const Descriptor *ElementDesc, size_t NumElements, unsigned EvalID, Form AllocForm) { + assert(ElementDesc->getMetadataSize() == 0); // Create a new descriptor for an array of the specified size and // element type. const Descriptor *D = allocateDescriptor( @@ -72,6 +73,7 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID, auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false); B->invokeCtor(); + assert(D->getMetadataSize() == sizeof(InlineDescriptor)); InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData()); ID->Desc = D; ID->IsActive = true; diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 5cc371c7ee495..73cc107b7dbff 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2896,9 +2896,7 @@ inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) { Block *B = Allocator.allocate(Desc, S.Ctx.getEvalID(), DynamicAllocator::Form::NonArray); assert(B); - S.Stk.push<Pointer>(B); - return true; } @@ -2923,8 +2921,7 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source, Allocator.allocate(Source, T, static_cast<size_t>(NumElements), S.Ctx.getEvalID(), DynamicAllocator::Form::Array); assert(B); - S.Stk.push<Pointer>(B, sizeof(InlineDescriptor)); - + S.Stk.push<Pointer>(B); return true; } @@ -2950,9 +2947,7 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc, Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements), S.Ctx.getEvalID(), DynamicAllocator::Form::Array); assert(B); - - S.Stk.push<Pointer>(B, sizeof(InlineDescriptor)); - + S.Stk.push<Pointer>(B); return true; } diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 55ac41736344d..b964906fb6594 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1655,27 +1655,27 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC, return false; } + bool IsArray = NumElems.ugt(1); std::optional<PrimType> ElemT = S.getContext().classify(ElemType); DynamicAllocator &Allocator = S.getAllocator(); if (ElemT) { - if (NumElems.ule(1)) { - const Descriptor *Desc = - S.P.createDescriptor(NewCall, *ElemT, Descriptor::InlineDescMD, - /*IsConst=*/false, /*IsTemporary=*/false, - /*IsMutable=*/false); - Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(), + if (IsArray) { + Block *B = Allocator.allocate(NewCall, *ElemT, NumElems.getZExtValue(), + S.Ctx.getEvalID(), DynamicAllocator::Form::Operator); assert(B); - - S.Stk.push<Pointer>(B); + S.Stk.push<Pointer>(Pointer(B).atIndex(0)); return true; } - assert(NumElems.ugt(1)); - Block *B = - Allocator.allocate(NewCall, *ElemT, NumElems.getZExtValue(), - S.Ctx.getEvalID(), DynamicAllocator::Form::Operator); + const Descriptor *Desc = + S.P.createDescriptor(NewCall, *ElemT, Descriptor::InlineDescMD, + /*IsConst=*/false, /*IsTemporary=*/false, + /*IsMutable=*/false); + Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(), + DynamicAllocator::Form::Operator); assert(B); + S.Stk.push<Pointer>(B); return true; } @@ -1683,21 +1683,22 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC, assert(!ElemT); // Structs etc. const Descriptor *Desc = S.P.createDescriptor( - NewCall, ElemType.getTypePtr(), Descriptor::InlineDescMD, + NewCall, ElemType.getTypePtr(), + IsArray ? std::nullopt : Descriptor::InlineDescMD, /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, /*Init=*/nullptr); - if (NumElems.ule(1)) { - Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(), - DynamicAllocator::Form::Operator); + if (IsArray) { + Block *B = + Allocator.allocate(Desc, NumElems.getZExtValue(), S.Ctx.getEvalID(), + DynamicAllocator::Form::Operator); assert(B); - S.Stk.push<Pointer>(B); + S.Stk.push<Pointer>(Pointer(B).atIndex(0)); return true; } - Block *B = - Allocator.allocate(Desc, NumElems.getZExtValue(), S.Ctx.getEvalID(), - DynamicAllocator::Form::Operator); + Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(), + DynamicAllocator::Form::Operator); assert(B); S.Stk.push<Pointer>(B); return true; diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index 833c9ef88d770..0754e259b7cb3 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -432,8 +432,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, return allocateDescriptor(D, *T, MDSize, IsTemporary, Descriptor::UnknownSize{}); } else { - const Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), - MDSize, IsConst, IsTemporary); + const Descriptor *Desc = createDescriptor( + D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary); if (!Desc) return nullptr; return allocateDescriptor(D, Desc, MDSize, IsTemporary, diff --git a/clang/test/AST/ByteCode/allocate-arrays.cpp b/clang/test/AST/ByteCode/allocate-arrays.cpp new file mode 100644 index 0000000000000..f1e5af6cab2aa --- /dev/null +++ b/clang/test/AST/ByteCode/allocate-arrays.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -std=c++2c -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -std=c++2c -verify=ref,both %s + + +/// This example used to cause an invalid read because allocating +/// an array needs to return a pointer to the first element, +/// not to the array. + +namespace std { + using size_t = decltype(sizeof(0)); + + template <class _Tp> + class allocator { + public: + typedef size_t size_type; + typedef _Tp value_type; + constexpr _Tp *allocate(size_t __n) { + return static_cast<_Tp *>(::operator new(__n * sizeof(_Tp))); + } + }; +} + +void *operator new(std::size_t, void *p) { return p; } +void* operator new[] (std::size_t, void* p) {return p;} + +namespace std { + template <class _Ep> + class initializer_list { + const _Ep *__begin_; + __SIZE_TYPE__ __size_; + + public: + typedef _Ep value_type; + typedef const _Ep &reference; + constexpr __SIZE_TYPE__ size() const noexcept { return __size_; } + constexpr const _Ep *begin() const noexcept { return __begin_; } + constexpr const _Ep *end() const noexcept { return __begin_ + __size_; } + }; +} + +template<typename T> +class vector { +public: + constexpr vector(std::initializer_list<T> Ts) { + A = B = std::allocator<T>{}.allocate(Ts.size()); // both-note {{heap allocation performed here}} + + new (A) T(*Ts.begin()); + } +private: + T *A = nullptr; + T *B = nullptr; +}; + +constexpr vector<vector<int>> ints = {{3}, {4}}; // both-error {{must be initialized by a constant expression}} \ + // both-note {{pointer to}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits