Skip to content

Commit

Permalink
[clang][bytecode] Check allocation size limit for operator new (llvm#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tbaederr authored and xgupta committed Oct 4, 2024
1 parent 513003c commit 81c795e
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 4 deletions.
11 changes: 10 additions & 1 deletion clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,16 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
return false;
}

// FIXME: CheckArraySize for NumElems?
// NB: The same check we're using in CheckArraySize()
if (NumElems.getActiveBits() >
ConstantArrayType::getMaxSizeBits(S.getASTContext()) ||
NumElems.ugt(Descriptor::MaxArrayElemBytes / ElemSize.getQuantity())) {
// FIXME: NoThrow check?
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_new_too_large)
<< NumElems.getZExtValue();
return false;
}

std::optional<PrimType> ElemT = S.getContext().classify(ElemType);
DynamicAllocator &Allocator = S.getAllocator();
Expand Down
16 changes: 14 additions & 2 deletions clang/lib/AST/ByteCode/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,26 @@ static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx,
V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, Ty);
}

static bool shouldSkipInBacktrace(const Function *F) {
if (F->isBuiltin())
return true;
if (F->isLambdaStaticInvoker())
return true;

const FunctionDecl *FD = F->getDecl();
if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New)
return true;
return false;
}

void InterpFrame::describe(llvm::raw_ostream &OS) const {
// We create frames for builtin functions as well, but we can't reliably
// diagnose them. The 'in call to' diagnostics for them add no value to the
// user _and_ it doesn't generally work since the argument types don't always
// match the function prototype. Just ignore them.
// Similarly, for lambda static invokers, we would just print __invoke().
if (const auto *F = getFunction();
F && (F->isBuiltin() || F->isLambdaStaticInvoker()))
if (const auto *F = getFunction(); F && shouldSkipInBacktrace(F))
return;

const Expr *CallExpr = Caller->getExpr(getRetPC());
Expand Down
27 changes: 26 additions & 1 deletion clang/test/AST/ByteCode/new-delete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,8 @@ namespace std {
using size_t = decltype(sizeof(0));
template<typename T> struct allocator {
constexpr T *allocate(size_t N) {
return (T*)__builtin_operator_new(sizeof(T) * N); // both-note 2{{allocation performed here}}
return (T*)__builtin_operator_new(sizeof(T) * N); // both-note 2{{allocation performed here}} \
// #alloc
}
constexpr void deallocate(void *p) {
__builtin_operator_delete(p); // both-note 2{{std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}} \
Expand Down Expand Up @@ -731,6 +732,30 @@ namespace Limits {
return n;
}
static_assert(dynarray<char>(5, 0) == 'f');


#if __LP64__
template <typename T>
struct S {
constexpr S(unsigned long long N)
: data(nullptr){
data = alloc.allocate(N); // both-note {{in call to 'this->alloc.allocate(18446744073709551615)}}
}
constexpr T operator[](std::size_t i) const {
return data[i];
}

constexpr ~S() {
alloc.deallocate(data);
}
std::allocator<T> alloc;
T* data;
};

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@#alloc {{cannot allocate array; evaluated array bound 2305843009213693951 is too large}} \
// both-note {{in call to}}
#endif
}

#else
Expand Down

0 comments on commit 81c795e

Please sign in to comment.