diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-22 15:06:13 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-22 15:21:11 +0000 |
| commit | fa83b7df5a0711bf5f4a5162cc33b0a4123a53c7 (patch) | |
| tree | dcfd24756256fcef21c43f19aa6e914455435806 /lib/CodeGen/ArrayHandler.cc | |
| parent | 353c38e23108856daa2c5d0e86443ac5136dcaa7 (diff) | |
fix(arrays): reject inverted bounds and check array allocation
Constant inverted bounds are a compile error and other bounds are checked
at run time. Allocation sizes use overflow-checked arithmetic, and a NULL
malloc (including the BYVAL copy) reports that the array is too large.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/ArrayHandler.cc')
| -rw-r--r-- | lib/CodeGen/ArrayHandler.cc | 61 |
1 files changed, 60 insertions, 1 deletions
diff --git a/lib/CodeGen/ArrayHandler.cc b/lib/CodeGen/ArrayHandler.cc index de07ff7..a4cbbe4 100644 --- a/lib/CodeGen/ArrayHandler.cc +++ b/lib/CodeGen/ArrayHandler.cc @@ -1,4 +1,5 @@ #include "cps/ArrayHandler.h" +#include "llvm/IR/Intrinsics.h" #include "cps/CodeGen.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" @@ -108,6 +109,15 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { if (!L || !R) return; + auto *ConstL = dyn_cast<ConstantInt>(L); + auto *ConstR = dyn_cast<ConstantInt>(R); + if (ConstL && ConstR && ConstR->getSExtValue() < ConstL->getSExtValue()) { + CG.reportError("Array %s upper bound %lld is less than lower bound %lld", Name.c_str(), + static_cast<long long>(ConstR->getSExtValue()), + static_cast<long long>(ConstL->getSExtValue())); + return; + } + Lows.push_back(L); Highs.push_back(R); } @@ -116,8 +126,10 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { const ArrayMetadata &Meta = defineMetadata(Name, ElemInfo, std::move(Lows), std::move(Highs), TotalElements); - Value *TotalBytes = Builder->CreateMul(TotalElements, Meta.ElementSizeC, Name + "_total_bytes"); + Value *TotalBytes = emitCheckedByteSize(Name, Meta.LowerBounds, Meta.UpperBounds, + Meta.ElementSizeC, Stmt->getLine()); CallInst *Ptr = Builder->CreateCall(MallocFunc, TotalBytes, Name + "_malloc"); + emitAllocCheck(Ptr, Name, Stmt->getLine()); FunctionType *MemsetType = FunctionType::get(PointerType::getUnqual(*TheContext), {PointerType::getUnqual(*TheContext), Type::getInt32Ty(*TheContext), Type::getInt64Ty(*TheContext)}, @@ -150,6 +162,53 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { CG.activateStackObject(Lifetime, TotalElements); } +Value *ArrayHandler::emitCheckedByteSize(const std::string &Name, + const std::vector<Value*> &Lows, + const std::vector<Value*> &Highs, + Constant *ElementSize, int Line) { + Type *I64 = Type::getInt64Ty(*TheContext); + Value *NameStr = Builder->CreateGlobalString(Name, "array_name", 0, TheModule); + Value *InvalidMsg = Builder->CreateGlobalString( + "[Fatal] line %d: Invalid bounds [%lld:%lld] for array %s\n", "err_array_bounds", 0, TheModule); + Value *TooLargeMsg = Builder->CreateGlobalString( + "[Fatal] line %d: Array %s is too large\n", "err_array_large", 0, TheModule); + + // Pass 1: upper >= lower in every dimension (this also keeps the + // differences below non-negative, so only the subtraction can overflow). + for (size_t I = 0; I < Lows.size(); ++I) { + if (isa<ConstantInt>(Lows[I]) && isa<ConstantInt>(Highs[I])) continue; // checked at compile time + Value *Inverted = Builder->CreateICmpSLT(Highs[I], Lows[I], Name + "_inverted"); + RuntimeChecker.emitCheck(Inverted, InvalidMsg, {RuntimeChecker.lineValue(Line), Lows[I], Highs[I], NameStr}); + } + + // Pass 2: sizes with overflow detection. + auto Overflowing = [&](Intrinsic::ID Id, Value *A, Value *B, Value *&Overflow) { + Value *Pair = Builder->CreateBinaryIntrinsic(Id, A, B); + Value *Flag = Builder->CreateExtractValue(Pair, 1); + Overflow = Overflow ? Builder->CreateOr(Overflow, Flag) : Flag; + return Builder->CreateExtractValue(Pair, 0); + }; + Value *Overflow = nullptr; + Value *Total = ConstantInt::get(I64, 1); + for (size_t I = 0; I < Lows.size(); ++I) { + Value *Diff = Overflowing(Intrinsic::ssub_with_overflow, Highs[I], Lows[I], Overflow); + Value *Dim = Builder->CreateAdd(Diff, ConstantInt::get(I64, 1), Name + "_dim_size"); + Total = Overflowing(Intrinsic::umul_with_overflow, Total, Dim, Overflow); + } + Value *Bytes = Overflowing(Intrinsic::umul_with_overflow, Total, ElementSize, Overflow); + RuntimeChecker.emitCheck(Overflow, TooLargeMsg, {RuntimeChecker.lineValue(Line), NameStr}); + return Bytes; +} + +void ArrayHandler::emitAllocCheck(Value *Ptr, const std::string &Name, int Line) { + Value *NameStr = Builder->CreateGlobalString(Name, "array_name", 0, TheModule); + Value *TooLargeMsg = Builder->CreateGlobalString( + "[Fatal] line %d: Array %s is too large\n", "err_array_large", 0, TheModule); + Value *IsNull = Builder->CreateICmpEQ( + Ptr, ConstantPointerNull::get(PointerType::getUnqual(*TheContext)), Name + "_alloc_failed"); + RuntimeChecker.emitCheck(IsNull, TooLargeMsg, {RuntimeChecker.lineValue(Line), NameStr}); +} + const ArrayMetadata &ArrayHandler::defineMetadata(const std::string &Name, const TypeInfo *ElemInfo, std::vector<Value*> Lows, std::vector<Value*> Highs, |
