Skip to content

Commit 22c9145

Browse files
[llvm] Fix handling of very large allocation in llvm.objectsize expansion
The internal structure used to carry intermediate computations hold signed values. If an object size happens to overflow signed values, we can get invalid result, so make sure this situation never happens. This is not very limitative as static allocation of such large values should scarcely happen.
1 parent d74127e commit 22c9145

File tree

4 files changed

+93
-7
lines changed

4 files changed

+93
-7
lines changed

llvm/include/llvm/Analysis/MemoryBuiltins.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ struct SizeOffsetAPInt : public SizeOffsetType<APInt, SizeOffsetAPInt> {
223223

224224
/// OffsetSpan - Used internally by \p ObjectSizeOffsetVisitor. Represents a
225225
/// point in memory as a pair of allocated bytes before and after it.
226+
///
227+
/// \c Before and \c After fields are signed values. It makes it possible to
228+
/// represent out-of-bound access, e.g. as a result of a GEP, at the expense of
229+
/// not being able to represent very large allocation.
226230
struct OffsetSpan {
227231
APInt Before; /// Number of allocated bytes before this point.
228232
APInt After; /// Number of allocated bytes after this point.
@@ -255,7 +259,7 @@ class ObjectSizeOffsetVisitor
255259
SmallDenseMap<Instruction *, OffsetSpan, 8> SeenInsts;
256260
unsigned InstructionsVisited;
257261

258-
APInt align(APInt Size, MaybeAlign Align);
262+
APInt alignAndCap(APInt Size, MaybeAlign Align);
259263

260264
static OffsetSpan unknown() { return OffsetSpan(); }
261265

llvm/lib/Analysis/MemoryBuiltins.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,13 @@ STATISTIC(ObjectVisitorArgument,
668668
STATISTIC(ObjectVisitorLoad,
669669
"Number of load instructions with unsolved size and offset");
670670

671-
APInt ObjectSizeOffsetVisitor::align(APInt Size, MaybeAlign Alignment) {
671+
/// Align \p Size according to \p Alignment. If \p Size is greater than
672+
/// getSignedMaxValue(), set it as unknown as we can only represent signed value
673+
/// in OffsetSpan.
674+
APInt ObjectSizeOffsetVisitor::alignAndCap(APInt Size, MaybeAlign Alignment) {
675+
if (Size.isNegative())
676+
return APInt();
677+
672678
if (Options.RoundToAlign && Alignment)
673679
return APInt(IntTyBits, alignTo(Size.getZExtValue(), *Alignment));
674680
return Size;
@@ -780,8 +786,9 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
780786
if (!isUIntN(IntTyBits, ElemSize.getKnownMinValue()))
781787
return ObjectSizeOffsetVisitor::unknown();
782788
APInt Size(IntTyBits, ElemSize.getKnownMinValue());
789+
783790
if (!I.isArrayAllocation())
784-
return OffsetSpan(Zero, align(Size, I.getAlign()));
791+
return OffsetSpan(Zero, alignAndCap(Size, I.getAlign()));
785792

786793
Value *ArraySize = I.getArraySize();
787794
if (const ConstantInt *C = dyn_cast<ConstantInt>(ArraySize)) {
@@ -791,8 +798,9 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
791798

792799
bool Overflow;
793800
Size = Size.umul_ov(NumElems, Overflow);
801+
794802
return Overflow ? ObjectSizeOffsetVisitor::unknown()
795-
: OffsetSpan(Zero, align(Size, I.getAlign()));
803+
: OffsetSpan(Zero, alignAndCap(Size, I.getAlign()));
796804
}
797805
return ObjectSizeOffsetVisitor::unknown();
798806
}
@@ -806,12 +814,16 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
806814
}
807815

808816
APInt Size(IntTyBits, DL.getTypeAllocSize(MemoryTy));
809-
return OffsetSpan(Zero, align(Size, A.getParamAlign()));
817+
return OffsetSpan(Zero, alignAndCap(Size, A.getParamAlign()));
810818
}
811819

812820
OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
813-
if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
821+
if (std::optional<APInt> Size = getAllocSize(&CB, TLI)) {
822+
// Very large unsigned value cannot be represented as OffsetSpan.
823+
if (Size->isNegative())
824+
return ObjectSizeOffsetVisitor::unknown();
814825
return OffsetSpan(Zero, *Size);
826+
}
815827
return ObjectSizeOffsetVisitor::unknown();
816828
}
817829

@@ -852,7 +864,7 @@ OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
852864
return ObjectSizeOffsetVisitor::unknown();
853865

854866
APInt Size(IntTyBits, DL.getTypeAllocSize(GV.getValueType()));
855-
return OffsetSpan(Zero, align(Size, GV.getAlign()));
867+
return OffsetSpan(Zero, alignAndCap(Size, GV.getAlign()));
856868
}
857869

858870
OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {

llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,20 @@ if.end:
118118
ret i64 %size
119119
}
120120

121+
define dso_local i64 @pick_max_large(i1 %c) local_unnamed_addr {
122+
; CHECK-LABEL: @pick_max_large(
123+
; CHECK-NEXT: [[BUFFER:%.*]] = alloca i8, i64 -7, align 1
124+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], ptr null, ptr [[BUFFER]]
125+
; CHECK-NEXT: ret i64 -1
126+
;
127+
%buffer = alloca i8, i64 -7 ; Actually a very large positive integer
128+
%s = select i1 %c, ptr null, ptr %buffer
129+
%objsize = tail call i64 @llvm.objectsize.i64.p0(ptr %s, i1 false, i1 false, i1 false)
130+
ret i64 %objsize
131+
132+
}
133+
134+
121135
define i64 @pick_negative_offset(i32 %n) {
122136
; CHECK-LABEL: @pick_negative_offset(
123137
; CHECK-NEXT: entry:

llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,62 @@ define i64 @out_of_bound_gep() {
195195
ret i64 %objsize
196196
}
197197

198+
define i64 @wrapping_gep() {
199+
; CHECK-LABEL: @wrapping_gep(
200+
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i64 4, align 1
201+
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i64 9223372036854775807
202+
; CHECK-NEXT: [[SLIDE_BIS:%.*]] = getelementptr i8, ptr [[SLIDE]], i64 9223372036854775807
203+
; CHECK-NEXT: ret i64 0
204+
;
205+
%obj = alloca i8, i64 4
206+
%slide = getelementptr i8, ptr %obj, i64 9223372036854775807
207+
%slide.bis = getelementptr i8, ptr %slide, i64 9223372036854775807
208+
%objsize = call i64 @llvm.objectsize.i64(ptr %slide.bis, i1 false, i1 false, i1 false)
209+
ret i64 %objsize
210+
}
211+
212+
define i64 @wrapping_gep_neg() {
213+
; CHECK-LABEL: @wrapping_gep_neg(
214+
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i64 9223372036854775807, align 1
215+
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i64 9223372036854775807
216+
; CHECK-NEXT: [[SLIDE_BIS:%.*]] = getelementptr i8, ptr [[SLIDE]], i64 3
217+
; CHECK-NEXT: [[SLIDE_TER:%.*]] = getelementptr i8, ptr [[SLIDE_BIS]], i64 -4
218+
; CHECK-NEXT: ret i64 1
219+
;
220+
%obj = alloca i8, i64 9223372036854775807
221+
%slide = getelementptr i8, ptr %obj, i64 9223372036854775807
222+
%slide.bis = getelementptr i8, ptr %slide, i64 3
223+
%slide.ter = getelementptr i8, ptr %slide.bis, i64 -4
224+
%objsize = call i64 @llvm.objectsize.i64(ptr %slide.ter, i1 false, i1 false, i1 false)
225+
ret i64 %objsize
226+
}
227+
228+
; We don't analyze allocations larger than platform's ptrdiff_t
229+
define i64 @large_alloca() {
230+
; CHECK-LABEL: @large_alloca(
231+
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i64 -9223372036854775808, align 1
232+
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i64 9223372036854775807
233+
; CHECK-NEXT: ret i64 -1
234+
;
235+
%obj = alloca i8, i64 9223372036854775808
236+
%slide = getelementptr i8, ptr %obj, i64 9223372036854775807
237+
%objsize = call i64 @llvm.objectsize.i64(ptr %slide, i1 false, i1 false, i1 false)
238+
ret i64 %objsize
239+
}
240+
241+
; We don't analyze allocations larger than platform's ptrdiff_t
242+
define i64 @large_malloc() {
243+
; CHECK-LABEL: @large_malloc(
244+
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @malloc(i64 -9223372036854775808)
245+
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i64 9223372036854775807
246+
; CHECK-NEXT: ret i64 -1
247+
;
248+
%obj = call ptr @malloc(i64 9223372036854775808)
249+
%slide = getelementptr i8, ptr %obj, i64 9223372036854775807
250+
%objsize = call i64 @llvm.objectsize.i64(ptr %slide, i1 false, i1 false, i1 false)
251+
ret i64 %objsize
252+
}
253+
198254
define i64 @out_of_bound_negative_gep() {
199255
; CHECK-LABEL: @out_of_bound_negative_gep(
200256
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i32 4, align 1

0 commit comments

Comments
 (0)