Skip to content

Commit 8842c80

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 8842c80

File tree

5 files changed

+200
-9
lines changed

5 files changed

+200
-9
lines changed

llvm/include/llvm/Analysis/MemoryBuiltins.h

Lines changed: 10 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.

llvm/lib/Analysis/MemoryBuiltins.cpp

Lines changed: 93 additions & 5 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.
@@ -709,14 +719,47 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
709719
// readjust the APInt as we pass it upwards in order for the APInt to match
710720
// the type the caller passed in.
711721
APInt Offset(InitialIntTyBits, 0);
712-
V = V->stripAndAccumulateConstantOffsets(
713-
DL, Offset, /* AllowNonInbounds */ true, /* AllowInvariantGroup */ true);
722+
723+
// External Analysis used to compute the Min/Max value of individual Offsets
724+
// within a GEP.
725+
auto OffsetRangeAnalysis =
726+
[this, V](Value &VOffset, APInt &Offset) {
727+
if (auto *C = dyn_cast<ConstantInt>(&VOffset)) {
728+
Offset = C->getValue();
729+
return true;
730+
}
731+
if (Options.EvalMode != ObjectSizeOpts::Mode::Min &&
732+
Options.EvalMode != ObjectSizeOpts::Mode::Max) {
733+
return false;
734+
}
735+
ConstantRange CR = computeConstantRange(
736+
&VOffset, /*ForSigned*/ true, /*UseInstrInfo*/ true, /*AC=*/nullptr,
737+
/*CtxtI=*/dyn_cast<Instruction>(V), /*DT=*/Options.DT);
738+
if (CR.isFullSet())
739+
return false;
740+
741+
if (Options.EvalMode == ObjectSizeOpts::Mode::Min) {
742+
Offset = CR.getSignedMax();
743+
// Upper bound actually unknown.
744+
if (Offset.isMaxSignedValue())
745+
return false;
746+
} else {
747+
Offset = CR.getSignedMin();
748+
// Lower bound actually unknown.
749+
if (Offset.isMinSignedValue())
750+
return false;
751+
}
752+
return true;
753+
};
754+
755+
V = const_cast<Value *>(V->stripAndAccumulateConstantOffsets(
756+
DL, Offset, /* AllowNonInbounds */ true, /* AllowInvariantGroup */ true,
757+
/*ExternalAnalysis=*/OffsetRangeAnalysis));
714758

715759
// Later we use the index type size and zero but it will match the type of the
716760
// value that is passed to computeImpl.
717761
IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
718762
Zero = APInt::getZero(IntTyBits);
719-
720763
OffsetSpan ORT = computeValue(V);
721764

722765
bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
@@ -794,6 +837,26 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
794837
Size = Size.umul_ov(NumElems, Overflow);
795838
return Overflow ? ObjectSizeOffsetVisitor::unknown()
796839
: OffsetSpan(Zero, align(Size, I.getAlign()));
840+
} else {
841+
ConstantRange CR =
842+
computeConstantRange(ArraySize, /*ForSigned*/ false,
843+
/*UseInstrInfo*/ true, /*AC=*/nullptr,
844+
/*CtxtI=*/&I, /*DT=*/Options.DT);
845+
if (CR.isFullSet())
846+
return ObjectSizeOffsetVisitor::unknown();
847+
APInt Bound;
848+
if (Options.EvalMode == ObjectSizeOpts::Mode::Max) {
849+
Bound = CR.getUnsignedMax();
850+
// Upper bound actually unknown.
851+
if (Bound.isMaxValue())
852+
return ObjectSizeOffsetVisitor::unknown();
853+
} else {
854+
Bound = CR.getUnsignedMin();
855+
// Lower bound actually unknown.
856+
if (Bound.isMinValue())
857+
return ObjectSizeOffsetVisitor::unknown();
858+
}
859+
return OffsetSpan(Zero, align(Bound, I.getAlign()));
797860
}
798861
return ObjectSizeOffsetVisitor::unknown();
799862
}
@@ -811,7 +874,32 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
811874
}
812875

813876
OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
814-
if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
877+
if (std::optional<APInt> Size =
878+
getAllocSize(&CB, TLI, [&CB, this](const Value *V) -> const Value * {
879+
if (!V->getType()->isIntegerTy())
880+
return V;
881+
if (isa<ConstantInt>(V))
882+
return V;
883+
ConstantRange CR = computeConstantRange(
884+
V, /*ForSigned*/ false, /*UseInstrInfo*/ true, /*AC=*/nullptr,
885+
/*CtxtI=*/&CB, /*DT=*/Options.DT);
886+
if (CR.isFullSet())
887+
return V;
888+
889+
APInt Bound;
890+
if (Options.EvalMode == ObjectSizeOpts::Mode::Max) {
891+
Bound = CR.getUnsignedMax();
892+
// Upper bound actually unknown.
893+
if (Bound.isMaxValue())
894+
return V;
895+
} else {
896+
Bound = CR.getUnsignedMin();
897+
// Lower bound actually unknown.
898+
if (Bound.isMinValue())
899+
return V;
900+
}
901+
return ConstantInt::get(V->getType(), Bound);
902+
}))
815903
return OffsetSpan(Zero, *Size);
816904
return ObjectSizeOffsetVisitor::unknown();
817905
}

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: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
define i64 @select_gep_offsets(i1 %cond) {
77+
; CHECK-LABEL: @select_gep_offsets(
78+
; CHECK-NEXT: [[PTR:%.*]] = alloca [10 x i8], i64 2, align 1
79+
; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[COND:%.*]], i32 0, i32 1
80+
; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr inbounds [10 x i8], ptr [[PTR]], i32 [[OFFSET]], i32 5
81+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 15, i64 5
82+
; CHECK-NEXT: ret i64 [[RES]]
83+
;
84+
%ptr = alloca [10 x i8], i64 2
85+
%offset = select i1 %cond, i32 0, i32 1
86+
%ptr.slide = getelementptr inbounds [10 x i8], ptr %ptr, i32 %offset, i32 5
87+
%objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 false, i1 true, i1 false)
88+
%objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 true, i1 true, i1 false)
89+
%res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
90+
ret i64 %res
91+
}
92+
93+
attributes #0 = { nounwind allocsize(0) }

0 commit comments

Comments
 (0)