Skip to content

[llvm] Bail out when meeting pointer with negative offset instead of … #120424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions llvm/lib/Analysis/MemoryBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -838,11 +838,14 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {

// We end up pointing on a location that's outside of the original object.
if (ORT.knownBefore() && ORT.Before.isNegative()) {
// This is UB, and we'd rather return an empty location then.
// This means that we *may* be accessing memory before the allocation.
// Conservatively return an unknown size.
//
// TODO: working with ranges instead of value would make it possible to take
// a better decision.
if (Options.EvalMode == ObjectSizeOpts::Mode::Min ||
Options.EvalMode == ObjectSizeOpts::Mode::Max) {
ORT.Before = APInt::getZero(ORT.Before.getBitWidth());
ORT.After = APInt::getZero(ORT.Before.getBitWidth());
return ObjectSizeOffsetVisitor::unknown();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the comment above incorrect? IIUC the case @mstorsjo shared doesn't count UB

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed; regularly with UB, when looking at various potential execution paths, the compiler can assume that the ones that are UB just won't happen at runtime. (Not sure how that translates best to this feature though, which is intended to protect against things at runtime that really are unintended.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at all, i'll update it. The idea would be 'if we are uncertain about the accuracy and the validity of the access, better be safe and bail out rather than return a potentially invalid result.

}
// Otherwise it's fine, caller can handle negative offset.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ define dso_local i64 @pick_max_one_oob(i1 %c0, i1 %c1) {
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[P_END:%.*]] = phi ptr [ [[P_ELSE]], [[IF_ELSE]] ], [ [[P_THEN]], [[IF_THEN]] ]
; CHECK-NEXT: [[OBJSIZE:%.*]] = select i1 [[C1:%.*]], i64 1, i64 0
; CHECK-NEXT: [[OBJSIZE:%.*]] = select i1 [[C1:%.*]], i64 -1, i64 0
; CHECK-NEXT: ret i64 [[OBJSIZE]]
;
%p = alloca [2 x i8], align 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ define i64 @select_neg_oob_offset(i1 %c0, i1 %c1) {
; CHECK-NEXT: [[PTR:%.*]] = alloca i8, i64 10, align 1
; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0:%.*]], i64 -3, i64 -4
; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[OFFSET]]
; CHECK-NEXT: ret i64 0
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1:%.*]], i64 -1, i64 0
; CHECK-NEXT: ret i64 [[RES]]
;
%ptr = alloca i8, i64 10
%offset = select i1 %c0, i64 -3, i64 -4
Expand Down Expand Up @@ -106,4 +107,25 @@ define i64 @select_gep_offsets(i1 %cond) {
ret i64 %res
}

define i64 @select_gep_oob_overapproximated_offsets(i1 %cond) {
; CHECK-LABEL: @select_gep_oob_overapproximated_offsets(
; CHECK-NEXT: [[BASE1:%.*]] = alloca [288 x i8], align 16
; CHECK-NEXT: [[SELECT0:%.*]] = select i1 [[COND:%.*]], i64 -4, i64 -64
; CHECK-NEXT: [[SELECT1:%.*]] = select i1 [[COND]], i64 16, i64 64
; CHECK-NEXT: [[GEP0:%.*]] = getelementptr inbounds nuw i8, ptr [[BASE1]], i64 [[SELECT1]]
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[GEP0]], i64 [[SELECT0]]
; CHECK-NEXT: ret i64 -1
;
%base1 = alloca [288 x i8], align 16
%select0 = select i1 %cond, i64 -4, i64 -64
%select1 = select i1 %cond, i64 16, i64 64
; This never actually goes oob, but because we approximate each select
; independently, this actually ranges in [16 - 64 ; 64 - 4] instead of [64 - 64; 16 - 4]
%gep0 = getelementptr inbounds nuw i8, ptr %base1, i64 %select1
%gep1 = getelementptr inbounds i8, ptr %gep0, i64 %select0
%call = call i64 @llvm.objectsize.i64.p0(ptr %gep1, i1 false, i1 true, i1 false)
ret i64 %call
}


attributes #0 = { nounwind allocsize(0) }
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ define i64 @wrapping_gep_neg(i1 %c) {
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i64 4, align 1
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i64 9223372036854775807
; CHECK-NEXT: [[SLIDE_BIS:%.*]] = getelementptr i8, ptr [[SLIDE]], i64 9223372036854775807
; CHECK-NEXT: ret i64 0
; CHECK-NEXT: ret i64 -1
;
%obj = alloca i8, i64 4
%slide = getelementptr i8, ptr %obj, i64 9223372036854775807
Expand Down Expand Up @@ -269,7 +269,7 @@ define i64 @out_of_bound_negative_gep(i1 %c) {
; CHECK-LABEL: @out_of_bound_negative_gep(
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i32 4, align 1
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i8 -8
; CHECK-NEXT: ret i64 0
; CHECK-NEXT: ret i64 -1
;
%obj = alloca i8, i32 4
%slide = getelementptr i8, ptr %obj, i8 -8
Expand Down
Loading