Skip to content

Commit 10feb40

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 10feb40

File tree

4 files changed

+105
-6
lines changed

4 files changed

+105
-6
lines changed

llvm/include/llvm/Analysis/MemoryBuiltins.h

Lines changed: 4 additions & 0 deletions
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.

llvm/lib/Analysis/MemoryBuiltins.cpp

Lines changed: 31 additions & 6 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+
/// 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.
671674
APInt ObjectSizeOffsetVisitor::align(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,6 +795,7 @@ 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())
784800
return OffsetSpan(Zero, align(Size, I.getAlign()));
785801

@@ -791,6 +807,7 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
791807

792808
bool Overflow;
793809
Size = Size.umul_ov(NumElems, Overflow);
810+
794811
return Overflow ? ObjectSizeOffsetVisitor::unknown()
795812
: OffsetSpan(Zero, align(Size, I.getAlign()));
796813
}
@@ -810,8 +827,12 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
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

@@ -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)