Skip to content

Commit b1277d2

Browse files
[llvm] Use computeConstantRange to improve llvm.objectsize computation
Using computeConstantRange, it is possible to compute valuable information for allocation functions, GEP and alloca, even in the presence of some dynamic information. llvm.objectsize plays an important role in _FORTIFY_SOURCE definitions, so improving its diagnostic in turns improves the security of compiled application. As a side note, as a result of recent optimization improvements, clang no longer passes https://github.com/serge-sans-paille/builtin_object_size-test-suite This commit restores the situation and greatly improves the scope of code handled by the static version of __builtin_object_size.
1 parent 2804762 commit b1277d2

File tree

5 files changed

+142
-7
lines changed

5 files changed

+142
-7
lines changed

llvm/include/llvm/Analysis/MemoryBuiltins.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ namespace llvm {
2929

3030
class AllocaInst;
3131
class AAResults;
32+
class AssumptionCache;
3233
class Argument;
3334
class ConstantPointerNull;
3435
class DataLayout;
36+
class DominatorTree;
3537
class ExtractElementInst;
3638
class ExtractValueInst;
3739
class GEPOperator;
@@ -160,8 +162,12 @@ struct ObjectSizeOpts {
160162
/// though they can't be evaluated. Otherwise, null is always considered to
161163
/// point to a 0 byte region of memory.
162164
bool NullIsUnknownSize = false;
163-
/// If set, used for more accurate evaluation
165+
/// If set, used for more accurate evaluation.
164166
AAResults *AA = nullptr;
167+
/// If set, used for more accurate evaluation.
168+
AssumptionCache *AC = nullptr;
169+
/// If set, used for more accurate evaluation.
170+
DominatorTree *DT = nullptr;
165171
};
166172

167173
/// Compute the size of the object pointed by Ptr. Returns true and the
@@ -186,6 +192,12 @@ Value *lowerObjectSizeCall(
186192
const TargetLibraryInfo *TLI, AAResults *AA, bool MustSucceed,
187193
SmallVectorImpl<Instruction *> *InsertedInstructions = nullptr);
188194

195+
Value *lowerObjectSizeCall(
196+
IntrinsicInst *ObjectSize, const DataLayout &DL,
197+
const TargetLibraryInfo *TLI, AAResults *AA, DominatorTree *DT,
198+
AssumptionCache *AC, bool MustSucceed,
199+
SmallVectorImpl<Instruction *> *InsertedInstructions = nullptr);
200+
189201
/// SizeOffsetType - A base template class for the object size visitors. Used
190202
/// here as a self-documenting way to handle the values rather than using a
191203
/// \p std::pair.
@@ -275,6 +287,7 @@ class ObjectSizeOffsetVisitor
275287
OffsetSpan visitExtractValueInst(ExtractValueInst &I);
276288
OffsetSpan visitGlobalAlias(GlobalAlias &GA);
277289
OffsetSpan visitGlobalVariable(GlobalVariable &GV);
290+
OffsetSpan visitGetElementPtr(GetElementPtrInst &GEP);
278291
OffsetSpan visitIntToPtrInst(IntToPtrInst &);
279292
OffsetSpan visitLoadInst(LoadInst &I);
280293
OffsetSpan visitPHINode(PHINode &);

llvm/lib/Analysis/MemoryBuiltins.cpp

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/ADT/STLExtras.h"
1717
#include "llvm/ADT/Statistic.h"
1818
#include "llvm/Analysis/AliasAnalysis.h"
19+
#include "llvm/Analysis/AssumptionCache.h"
1920
#include "llvm/Analysis/TargetFolder.h"
2021
#include "llvm/Analysis/TargetLibraryInfo.h"
2122
#include "llvm/Analysis/Utils/Local.h"
@@ -25,6 +26,7 @@
2526
#include "llvm/IR/Constants.h"
2627
#include "llvm/IR/DataLayout.h"
2728
#include "llvm/IR/DerivedTypes.h"
29+
#include "llvm/IR/Dominators.h"
2830
#include "llvm/IR/Function.h"
2931
#include "llvm/IR/GlobalAlias.h"
3032
#include "llvm/IR/GlobalVariable.h"
@@ -590,19 +592,31 @@ Value *llvm::lowerObjectSizeCall(IntrinsicInst *ObjectSize,
590592
const TargetLibraryInfo *TLI,
591593
bool MustSucceed) {
592594
return lowerObjectSizeCall(ObjectSize, DL, TLI, /*AAResults=*/nullptr,
593-
MustSucceed);
595+
/*DT=*/nullptr,
596+
/*AC=*/nullptr, MustSucceed);
594597
}
595598

596599
Value *llvm::lowerObjectSizeCall(
597600
IntrinsicInst *ObjectSize, const DataLayout &DL,
598601
const TargetLibraryInfo *TLI, AAResults *AA, bool MustSucceed,
599602
SmallVectorImpl<Instruction *> *InsertedInstructions) {
603+
return lowerObjectSizeCall(ObjectSize, DL, TLI, AA, /*DT=*/nullptr,
604+
/*AC=*/nullptr, MustSucceed, InsertedInstructions);
605+
}
606+
607+
Value *llvm::lowerObjectSizeCall(
608+
IntrinsicInst *ObjectSize, const DataLayout &DL,
609+
const TargetLibraryInfo *TLI, AAResults *AA, DominatorTree *DT,
610+
AssumptionCache *AC, bool MustSucceed,
611+
SmallVectorImpl<Instruction *> *InsertedInstructions) {
600612
assert(ObjectSize->getIntrinsicID() == Intrinsic::objectsize &&
601613
"ObjectSize must be a call to llvm.objectsize!");
602614

603615
bool MaxVal = cast<ConstantInt>(ObjectSize->getArgOperand(1))->isZero();
604616
ObjectSizeOpts EvalOptions;
605617
EvalOptions.AA = AA;
618+
EvalOptions.DT = DT;
619+
EvalOptions.AC = AC;
606620

607621
// Unless we have to fold this to something, try to be as accurate as
608622
// possible.
@@ -716,7 +730,6 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
716730
// value that is passed to computeImpl.
717731
IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
718732
Zero = APInt::getZero(IntTyBits);
719-
720733
OffsetSpan ORT = computeValue(V);
721734

722735
bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
@@ -794,6 +807,19 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
794807
Size = Size.umul_ov(NumElems, Overflow);
795808
return Overflow ? ObjectSizeOffsetVisitor::unknown()
796809
: OffsetSpan(Zero, align(Size, I.getAlign()));
810+
} else {
811+
ConstantRange CR = computeConstantRange(ArraySize, /*ForSigned*/ false,
812+
/*UseInstrInfo*/ true,
813+
/*AssumptionCache=*/Options.AC,
814+
/*CtxtI=*/&I, /*DT=*/Options.DT);
815+
if (CR.isFullSet())
816+
return ObjectSizeOffsetVisitor::unknown();
817+
APInt Bound;
818+
if (Options.EvalMode == ObjectSizeOpts::Mode::Max)
819+
Bound = CR.getUnsignedMax();
820+
else
821+
Bound = CR.getUnsignedMin();
822+
return OffsetSpan(Zero, align(Bound, I.getAlign()));
797823
}
798824
return ObjectSizeOffsetVisitor::unknown();
799825
}
@@ -811,7 +837,23 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
811837
}
812838

813839
OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
814-
if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
840+
if (std::optional<APInt> Size =
841+
getAllocSize(&CB, TLI, [&CB, this](const Value *V) -> const Value * {
842+
if (!V->getType()->isIntegerTy())
843+
return V;
844+
ConstantRange CR = computeConstantRange(
845+
V, /*ForSigned*/ false, /*UseInstrInfo*/ true,
846+
/*AssumptionCache=*/Options.AC,
847+
/*CtxtI=*/&CB, /*DT=*/Options.DT);
848+
if (CR.isFullSet())
849+
return V;
850+
APInt Bound;
851+
if (Options.EvalMode == ObjectSizeOpts::Mode::Max)
852+
Bound = CR.getUnsignedMax();
853+
else
854+
Bound = CR.getUnsignedMin();
855+
return ConstantInt::get(V->getType(), Bound);
856+
}))
815857
return OffsetSpan(Zero, *Size);
816858
return ObjectSizeOffsetVisitor::unknown();
817859
}
@@ -856,6 +898,42 @@ OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
856898
return OffsetSpan(Zero, align(Size, GV.getAlign()));
857899
}
858900

901+
OffsetSpan ObjectSizeOffsetVisitor::visitGetElementPtr(GetElementPtrInst &GEP) {
902+
OffsetSpan PtrData = computeImpl(GEP.getPointerOperand());
903+
if (!PtrData.bothKnown())
904+
return ObjectSizeOffsetVisitor::unknown();
905+
906+
if (Options.EvalMode == ObjectSizeOpts::Mode::Min ||
907+
Options.EvalMode == ObjectSizeOpts::Mode::Max) {
908+
unsigned BitWidth = PtrData.After.getBitWidth();
909+
APInt ConstantOffset = Zero;
910+
SmallMapVector<Value *, APInt, 4> VariableOffsets;
911+
if (!GEP.collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
912+
return ObjectSizeOffsetVisitor::unknown();
913+
914+
ConstantRange AccumulatedRange = ConstantOffset;
915+
for (auto const &VO : VariableOffsets) {
916+
ConstantRange CR = computeConstantRange(
917+
VO.first, /*ForSigned*/ true, /*UseInstrInfo*/ true,
918+
/*AssumptionCache=*/Options.AC,
919+
/*CtxtI=*/&GEP, /*DT=*/Options.DT);
920+
if (CR.isFullSet())
921+
return ObjectSizeOffsetVisitor::unknown();
922+
923+
AccumulatedRange = AccumulatedRange.add(CR.multiply(VO.second));
924+
}
925+
926+
APInt Bound;
927+
if (Options.EvalMode == ObjectSizeOpts::Mode::Min)
928+
Bound = AccumulatedRange.getSignedMax();
929+
else
930+
Bound = AccumulatedRange.getSignedMin();
931+
932+
return {PtrData.Before + Bound, PtrData.After - Bound};
933+
}
934+
return ObjectSizeOffsetVisitor::unknown();
935+
}
936+
859937
OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
860938
// clueless
861939
return ObjectSizeOffsetVisitor::unknown();

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3317,8 +3317,9 @@ Instruction *InstCombinerImpl::visitAllocSite(Instruction &MI) {
33173317
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
33183318
if (II->getIntrinsicID() == Intrinsic::objectsize) {
33193319
SmallVector<Instruction *> InsertedInstructions;
3320-
Value *Result = lowerObjectSizeCall(
3321-
II, DL, &TLI, AA, /*MustSucceed=*/true, &InsertedInstructions);
3320+
Value *Result =
3321+
lowerObjectSizeCall(II, DL, &TLI, AA, &DT, &AC,
3322+
/*MustSucceed=*/true, &InsertedInstructions);
33223323
for (Instruction *Inserted : InsertedInstructions)
33233324
Worklist.add(Inserted);
33243325
replaceInstUsesWith(*I, Result);

llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ bool llvm::lowerConstantIntrinsics(Function &F, const TargetLibraryInfo &TLI,
144144
IsConstantIntrinsicsHandled++;
145145
break;
146146
case Intrinsic::objectsize:
147-
NewValue = lowerObjectSizeCall(II, DL, &TLI, true);
147+
NewValue = lowerObjectSizeCall(II, DL, &TLI, /*AA=*/nullptr, DT,
148+
/*AC=*/nullptr, true);
148149
LLVM_DEBUG(dbgs() << "Folding " << *II << " to " << *NewValue << "\n");
149150
ObjectSizeIntrinsicsHandled++;
150151
break;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -passes=lower-constant-intrinsics -S < %s | FileCheck %s
3+
4+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
5+
target triple = "x86_64-unknown-linux-gnu"
6+
7+
declare i64 @llvm.objectsize.i64.p0(ptr, i1 immarg, i1 immarg, i1 immarg)
8+
declare noalias ptr @malloc(i64 noundef) #0
9+
10+
; TODO: computeConstantRange is not able to see through this simple condition.
11+
define i64 @phi_malloc_size(i1 %cond, i64 %size) {
12+
; CHECK-LABEL: @phi_malloc_size(
13+
; CHECK-NEXT: entry:
14+
; CHECK-NEXT: [[SHIFTED:%.*]] = add i64 [[SIZE:%.*]], -11
15+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[SHIFTED]], -8
16+
; CHECK-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[IF_END:%.*]]
17+
; CHECK: if.end:
18+
; CHECK-NEXT: [[MEM:%.*]] = tail call noalias ptr @malloc(i64 noundef [[SIZE]])
19+
; CHECK-NEXT: [[RES1:%.*]] = select i1 [[COND:%.*]], i64 -1, i64 0
20+
; CHECK-NEXT: br label [[RETURN]]
21+
; CHECK: return:
22+
; CHECK-NEXT: [[RES:%.*]] = phi i64 [ [[RES1]], [[IF_END]] ], [ -1, [[ENTRY:%.*]] ]
23+
; CHECK-NEXT: ret i64 [[RES]]
24+
;
25+
entry:
26+
%shifted = add i64 %size, -11
27+
%valid = icmp ult i64 %shifted, -8
28+
br i1 %valid, label %return, label %if.end
29+
30+
if.end:
31+
%ptr = tail call noalias ptr @malloc(i64 noundef %size)
32+
%objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 false, i1 false, i1 false)
33+
%objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 true, i1 false, i1 false)
34+
%res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
35+
br label %return
36+
37+
return:
38+
%res.phi = phi i64 [%res, %if.end], [-1, %entry]
39+
ret i64 %res.phi
40+
}
41+
42+
attributes #0 = { nounwind allocsize(0) }

0 commit comments

Comments
 (0)