Skip to content

Commit a7e4478

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 a7e4478

File tree

4 files changed

+111
-12
lines changed

4 files changed

+111
-12
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: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -668,10 +668,14 @@ 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) {
672675
if (Options.RoundToAlign && Alignment)
673-
return APInt(IntTyBits, alignTo(Size.getZExtValue(), *Alignment));
674-
return Size;
676+
Size = APInt(IntTyBits, alignTo(Size.getZExtValue(), *Alignment));
677+
678+
return Size.isNegative() ? APInt() : Size;
675679
}
676680

677681
ObjectSizeOffsetVisitor::ObjectSizeOffsetVisitor(const DataLayout &DL,
@@ -733,8 +737,19 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
733737
ORT.After = APInt();
734738
}
735739
// If the computed bound is "unknown" we cannot add the stripped offset.
736-
return {(ORT.knownBefore() ? ORT.Before + Offset : ORT.Before),
737-
(ORT.knownAfter() ? ORT.After - Offset : ORT.After)};
740+
if (ORT.knownBefore()) {
741+
bool Overflow;
742+
ORT.Before = ORT.Before.sadd_ov(Offset, Overflow);
743+
if (Overflow)
744+
ORT.Before = APInt();
745+
}
746+
if (ORT.knownAfter()) {
747+
bool Overflow;
748+
ORT.After = ORT.After.ssub_ov(Offset, Overflow);
749+
if (Overflow)
750+
ORT.After = APInt();
751+
}
752+
return ORT;
738753
}
739754

740755
OffsetSpan ObjectSizeOffsetVisitor::computeValue(Value *V) {
@@ -780,8 +795,9 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
780795
if (!isUIntN(IntTyBits, ElemSize.getKnownMinValue()))
781796
return ObjectSizeOffsetVisitor::unknown();
782797
APInt Size(IntTyBits, ElemSize.getKnownMinValue());
798+
783799
if (!I.isArrayAllocation())
784-
return OffsetSpan(Zero, align(Size, I.getAlign()));
800+
return OffsetSpan(Zero, alignAndCap(Size, I.getAlign()));
785801

786802
Value *ArraySize = I.getArraySize();
787803
if (const ConstantInt *C = dyn_cast<ConstantInt>(ArraySize)) {
@@ -791,8 +807,9 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
791807

792808
bool Overflow;
793809
Size = Size.umul_ov(NumElems, Overflow);
810+
794811
return Overflow ? ObjectSizeOffsetVisitor::unknown()
795-
: OffsetSpan(Zero, align(Size, I.getAlign()));
812+
: OffsetSpan(Zero, alignAndCap(Size, I.getAlign()));
796813
}
797814
return ObjectSizeOffsetVisitor::unknown();
798815
}
@@ -806,12 +823,16 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
806823
}
807824

808825
APInt Size(IntTyBits, DL.getTypeAllocSize(MemoryTy));
809-
return OffsetSpan(Zero, align(Size, A.getParamAlign()));
826+
return OffsetSpan(Zero, alignAndCap(Size, A.getParamAlign()));
810827
}
811828

812829
OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
813-
if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
830+
if (std::optional<APInt> Size = getAllocSize(&CB, TLI)) {
831+
// Very large unsigned value cannot be represented as OffsetSpan.
832+
if (Size->isNegative())
833+
return ObjectSizeOffsetVisitor::unknown();
814834
return OffsetSpan(Zero, *Size);
835+
}
815836
return ObjectSizeOffsetVisitor::unknown();
816837
}
817838

@@ -852,7 +873,7 @@ OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
852873
return ObjectSizeOffsetVisitor::unknown();
853874

854875
APInt Size(IntTyBits, DL.getTypeAllocSize(GV.getValueType()));
855-
return OffsetSpan(Zero, align(Size, GV.getAlign()));
876+
return OffsetSpan(Zero, alignAndCap(Size, GV.getAlign()));
856877
}
857878

858879
OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
@@ -944,7 +965,11 @@ OffsetSpan ObjectSizeOffsetVisitor::findLoadOffsetRange(
944965
if (!C)
945966
return Unknown();
946967

947-
return Known({APInt(C->getValue().getBitWidth(), 0), C->getValue()});
968+
APInt CSize = C->getValue();
969+
if (CSize.isNegative())
970+
return Unknown();
971+
972+
return Known({APInt(CSize.getBitWidth(), 0), CSize});
948973
}
949974

950975
return Unknown();

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)