Skip to content

Commit 95739c6

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 af6ebb7 commit 95739c6

File tree

5 files changed

+190
-7
lines changed

5 files changed

+190
-7
lines changed

llvm/include/llvm/Analysis/MemoryBuiltins.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class AAResults;
3232
class Argument;
3333
class ConstantPointerNull;
3434
class DataLayout;
35+
class DominatorTree;
3536
class ExtractElementInst;
3637
class ExtractValueInst;
3738
class GEPOperator;
@@ -160,8 +161,10 @@ struct ObjectSizeOpts {
160161
/// though they can't be evaluated. Otherwise, null is always considered to
161162
/// point to a 0 byte region of memory.
162163
bool NullIsUnknownSize = false;
163-
/// If set, used for more accurate evaluation
164+
/// If set, used for more accurate evaluation.
164165
AAResults *AA = nullptr;
166+
/// If set, used for more accurate evaluation.
167+
DominatorTree *DT = nullptr;
165168
};
166169

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

192+
Value *lowerObjectSizeCall(
193+
IntrinsicInst *ObjectSize, const DataLayout &DL,
194+
const TargetLibraryInfo *TLI, AAResults *AA, DominatorTree *DT,
195+
bool MustSucceed,
196+
SmallVectorImpl<Instruction *> *InsertedInstructions = nullptr);
197+
189198
/// SizeOffsetType - A base template class for the object size visitors. Used
190199
/// here as a self-documenting way to handle the values rather than using a
191200
/// \p std::pair.
@@ -275,6 +284,7 @@ class ObjectSizeOffsetVisitor
275284
OffsetSpan visitExtractValueInst(ExtractValueInst &I);
276285
OffsetSpan visitGlobalAlias(GlobalAlias &GA);
277286
OffsetSpan visitGlobalVariable(GlobalVariable &GV);
287+
OffsetSpan visitGetElementPtr(GetElementPtrInst &GEP);
278288
OffsetSpan visitIntToPtrInst(IntToPtrInst &);
279289
OffsetSpan visitLoadInst(LoadInst &I);
280290
OffsetSpan visitPHINode(PHINode &);

llvm/lib/Analysis/MemoryBuiltins.cpp

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/IR/Constants.h"
2626
#include "llvm/IR/DataLayout.h"
2727
#include "llvm/IR/DerivedTypes.h"
28+
#include "llvm/IR/Dominators.h"
2829
#include "llvm/IR/Function.h"
2930
#include "llvm/IR/GlobalAlias.h"
3031
#include "llvm/IR/GlobalVariable.h"
@@ -590,19 +591,28 @@ Value *llvm::lowerObjectSizeCall(IntrinsicInst *ObjectSize,
590591
const TargetLibraryInfo *TLI,
591592
bool MustSucceed) {
592593
return lowerObjectSizeCall(ObjectSize, DL, TLI, /*AAResults=*/nullptr,
593-
MustSucceed);
594+
/*DT=*/nullptr, MustSucceed);
594595
}
595596

596597
Value *llvm::lowerObjectSizeCall(
597598
IntrinsicInst *ObjectSize, const DataLayout &DL,
598599
const TargetLibraryInfo *TLI, AAResults *AA, bool MustSucceed,
599600
SmallVectorImpl<Instruction *> *InsertedInstructions) {
601+
return lowerObjectSizeCall(ObjectSize, DL, TLI, AA, /*DT=*/nullptr,
602+
MustSucceed, InsertedInstructions);
603+
}
604+
605+
Value *llvm::lowerObjectSizeCall(
606+
IntrinsicInst *ObjectSize, const DataLayout &DL,
607+
const TargetLibraryInfo *TLI, AAResults *AA, DominatorTree *DT,
608+
bool MustSucceed, SmallVectorImpl<Instruction *> *InsertedInstructions) {
600609
assert(ObjectSize->getIntrinsicID() == Intrinsic::objectsize &&
601610
"ObjectSize must be a call to llvm.objectsize!");
602611

603612
bool MaxVal = cast<ConstantInt>(ObjectSize->getArgOperand(1))->isZero();
604613
ObjectSizeOpts EvalOptions;
605614
EvalOptions.AA = AA;
615+
EvalOptions.DT = DT;
606616

607617
// Unless we have to fold this to something, try to be as accurate as
608618
// possible.
@@ -716,7 +726,6 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
716726
// value that is passed to computeImpl.
717727
IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
718728
Zero = APInt::getZero(IntTyBits);
719-
720729
OffsetSpan ORT = computeValue(V);
721730

722731
bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
@@ -794,6 +803,26 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
794803
Size = Size.umul_ov(NumElems, Overflow);
795804
return Overflow ? ObjectSizeOffsetVisitor::unknown()
796805
: OffsetSpan(Zero, align(Size, I.getAlign()));
806+
} else {
807+
ConstantRange CR =
808+
computeConstantRange(ArraySize, /*ForSigned*/ false,
809+
/*UseInstrInfo*/ true, /*AC=*/nullptr,
810+
/*CtxtI=*/&I, /*DT=*/Options.DT);
811+
if (CR.isFullSet())
812+
return ObjectSizeOffsetVisitor::unknown();
813+
APInt Bound;
814+
if (Options.EvalMode == ObjectSizeOpts::Mode::Max) {
815+
Bound = CR.getUnsignedMax();
816+
// Upper bound actually unknown.
817+
if (Bound.isMaxValue())
818+
return ObjectSizeOffsetVisitor::unknown();
819+
} else {
820+
Bound = CR.getUnsignedMin();
821+
// Lower bound actually unknown.
822+
if (Bound.isMinValue())
823+
return ObjectSizeOffsetVisitor::unknown();
824+
}
825+
return OffsetSpan(Zero, align(Bound, I.getAlign()));
797826
}
798827
return ObjectSizeOffsetVisitor::unknown();
799828
}
@@ -811,7 +840,32 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
811840
}
812841

813842
OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
814-
if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
843+
if (std::optional<APInt> Size =
844+
getAllocSize(&CB, TLI, [&CB, this](const Value *V) -> const Value * {
845+
if (!V->getType()->isIntegerTy())
846+
return V;
847+
if (isa<ConstantInt>(V))
848+
return V;
849+
ConstantRange CR = computeConstantRange(
850+
V, /*ForSigned*/ false, /*UseInstrInfo*/ true, /*AC=*/nullptr,
851+
/*CtxtI=*/&CB, /*DT=*/Options.DT);
852+
if (CR.isFullSet())
853+
return V;
854+
855+
APInt Bound;
856+
if (Options.EvalMode == ObjectSizeOpts::Mode::Max) {
857+
Bound = CR.getUnsignedMax();
858+
// Upper bound actually unknown.
859+
if (Bound.isMaxValue())
860+
return V;
861+
} else {
862+
Bound = CR.getUnsignedMin();
863+
// Lower bound actually unknown.
864+
if (Bound.isMinValue())
865+
return V;
866+
}
867+
return ConstantInt::get(V->getType(), Bound);
868+
}))
815869
return OffsetSpan(Zero, *Size);
816870
return ObjectSizeOffsetVisitor::unknown();
817871
}
@@ -856,6 +910,48 @@ OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
856910
return OffsetSpan(Zero, align(Size, GV.getAlign()));
857911
}
858912

913+
OffsetSpan ObjectSizeOffsetVisitor::visitGetElementPtr(GetElementPtrInst &GEP) {
914+
OffsetSpan PtrData = computeImpl(GEP.getPointerOperand());
915+
if (!PtrData.bothKnown())
916+
return ObjectSizeOffsetVisitor::unknown();
917+
918+
if (Options.EvalMode == ObjectSizeOpts::Mode::Min ||
919+
Options.EvalMode == ObjectSizeOpts::Mode::Max) {
920+
unsigned BitWidth = PtrData.After.getBitWidth();
921+
APInt ConstantOffset = Zero;
922+
SmallMapVector<Value *, APInt, 4> VariableOffsets;
923+
if (!GEP.collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
924+
return ObjectSizeOffsetVisitor::unknown();
925+
926+
ConstantRange AccumulatedRange = ConstantOffset;
927+
for (auto const &VO : VariableOffsets) {
928+
ConstantRange CR = computeConstantRange(
929+
VO.first, /*ForSigned*/ true, /*UseInstrInfo*/ true, /*AC=*/nullptr,
930+
/*CtxtI=*/&GEP, /*DT=*/Options.DT);
931+
if (CR.isFullSet())
932+
return ObjectSizeOffsetVisitor::unknown();
933+
934+
AccumulatedRange = AccumulatedRange.add(CR.multiply(VO.second));
935+
}
936+
937+
APInt Bound;
938+
if (Options.EvalMode == ObjectSizeOpts::Mode::Min) {
939+
Bound = AccumulatedRange.getSignedMax();
940+
// Upper bound actually unknown.
941+
if (Bound.isMaxSignedValue())
942+
return ObjectSizeOffsetVisitor::unknown();
943+
} else {
944+
Bound = AccumulatedRange.getSignedMin();
945+
// Lower bound actually unknown.
946+
if (Bound.isMinSignedValue())
947+
return ObjectSizeOffsetVisitor::unknown();
948+
}
949+
950+
return {PtrData.Before + Bound, PtrData.After - Bound};
951+
}
952+
return ObjectSizeOffsetVisitor::unknown();
953+
}
954+
859955
OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
860956
// clueless
861957
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,
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ bool llvm::lowerConstantIntrinsics(Function &F, const TargetLibraryInfo &TLI,
143143
IsConstantIntrinsicsHandled++;
144144
break;
145145
case Intrinsic::objectsize:
146-
NewValue = lowerObjectSizeCall(II, DL, &TLI, true);
146+
NewValue = lowerObjectSizeCall(II, DL, &TLI, /*AA=*/nullptr, DT, true);
147147
LLVM_DEBUG(dbgs() << "Folding " << *II << " to " << *NewValue << "\n");
148148
ObjectSizeIntrinsicsHandled++;
149149
break;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
define i64 @select_alloc_size(i1 %cond) {
11+
; CHECK-LABEL: @select_alloc_size(
12+
; CHECK-NEXT: [[SIZE:%.*]] = select i1 [[COND:%.*]], i64 3, i64 4
13+
; CHECK-NEXT: [[PTR:%.*]] = alloca i8, i64 [[SIZE]], align 1
14+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 4, i64 3
15+
; CHECK-NEXT: ret i64 [[RES]]
16+
;
17+
%size = select i1 %cond, i64 3, i64 4
18+
%ptr = alloca i8, i64 %size
19+
%objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 false, i1 true, i1 false)
20+
%objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 true, i1 true, i1 false)
21+
%res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
22+
ret i64 %res
23+
}
24+
25+
define i64 @select_malloc_size(i1 %cond) {
26+
; CHECK-LABEL: @select_malloc_size(
27+
; CHECK-NEXT: [[SIZE:%.*]] = select i1 [[COND:%.*]], i64 3, i64 4
28+
; CHECK-NEXT: [[PTR:%.*]] = call noalias ptr @malloc(i64 noundef [[SIZE]])
29+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 4, i64 3
30+
; CHECK-NEXT: ret i64 [[RES]]
31+
;
32+
%size = select i1 %cond, i64 3, i64 4
33+
%ptr = call noalias ptr @malloc(i64 noundef %size)
34+
%objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 false, i1 true, i1 false)
35+
%objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 true, i1 true, i1 false)
36+
%res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
37+
ret i64 %res
38+
}
39+
40+
define i64 @select_gep_offset(i1 %cond) {
41+
; CHECK-LABEL: @select_gep_offset(
42+
; CHECK-NEXT: [[PTR:%.*]] = alloca i8, i64 10, align 1
43+
; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[COND:%.*]], i64 3, i64 4
44+
; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[OFFSET]]
45+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 7, i64 6
46+
; CHECK-NEXT: ret i64 [[RES]]
47+
;
48+
%ptr = alloca i8, i64 10
49+
%offset = select i1 %cond, i64 3, i64 4
50+
%ptr.slide = getelementptr inbounds i8, ptr %ptr, i64 %offset
51+
%objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 false, i1 true, i1 false)
52+
%objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 true, i1 true, i1 false)
53+
%res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
54+
ret i64 %res
55+
}
56+
57+
define i64 @select_gep_neg_offset(i1 %cond) {
58+
; CHECK-LABEL: @select_gep_neg_offset(
59+
; CHECK-NEXT: [[PTR:%.*]] = alloca i8, i64 10, align 1
60+
; CHECK-NEXT: [[PTR_SLIDE_1:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 5
61+
; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[COND:%.*]], i64 -3, i64 -4
62+
; CHECK-NEXT: [[PTR_SLIDE_2:%.*]] = getelementptr inbounds i8, ptr [[PTR_SLIDE_1]], i64 [[OFFSET]]
63+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 9, i64 8
64+
; CHECK-NEXT: ret i64 [[RES]]
65+
;
66+
%ptr = alloca i8, i64 10
67+
%ptr.slide.1 = getelementptr inbounds i8, ptr %ptr, i64 5
68+
%offset = select i1 %cond, i64 -3, i64 -4
69+
%ptr.slide.2 = getelementptr inbounds i8, ptr %ptr.slide.1, i64 %offset
70+
%objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide.2, i1 false, i1 true, i1 false)
71+
%objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide.2, i1 true, i1 true, i1 false)
72+
%res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
73+
ret i64 %res
74+
}
75+
76+
attributes #0 = { nounwind allocsize(0) }

0 commit comments

Comments
 (0)