Skip to content

Commit 6983741

Browse files
committed
[Polly] Fix use-after-free.
VirtualUse of type UseKind::Inter expects the definition of a llvm::Value to be represented in another statement. In the bug report that statement has been removed due to its domain being empty. Scop::InstStmtMap for the llvm::Value's defintion still pointed to the removed statement, which resulted in the use-after-free. The defintion statement was removed by Simplify because it was considered to not be reachable by other uses; trivially because it is never executed due to its empty domain. However, no such thing happend to the using statement using the value altough its domain is also empty. Fix by always removing statements with empty domains in Simplify since these are not properly analyzable. A UseKind::Inter should always have a statement with its defintion due to LLVM's SSA form. Scop::removeStmtNotInDomainMap() also removes statements with empty domains but does so without considering the context as used by Simplify's analyzes. In another angle, InstStmtMap pointing to removed statements should not happen either and ForwardOpTree would have bailed out if the llvm::Value definition was not represented by a statement. This will be corrected in a followup-commit. This fixes llvm.org/PR47098
1 parent 901e331 commit 6983741

File tree

3 files changed

+136
-16
lines changed

3 files changed

+136
-16
lines changed

polly/include/polly/ScopInfo.h

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,18 +1949,6 @@ class Scop {
19491949
void addScopStmt(Region *R, StringRef Name, Loop *SurroundingLoop,
19501950
std::vector<Instruction *> EntryBlockInstructions);
19511951

1952-
/// Remove statements from the list of scop statements.
1953-
///
1954-
/// @param ShouldDelete A function that returns true if the statement passed
1955-
/// to it should be deleted.
1956-
/// @param AfterHoisting If true, also remove from data access lists.
1957-
/// These lists are filled during
1958-
/// ScopBuilder::buildAccessRelations. Therefore, if this
1959-
/// method is called before buildAccessRelations, false
1960-
/// must be passed.
1961-
void removeStmts(std::function<bool(ScopStmt &)> ShouldDelete,
1962-
bool AfterHoisting = true);
1963-
19641952
/// Removes @p Stmt from the StmtMap.
19651953
void removeFromStmtMap(ScopStmt &Stmt);
19661954

@@ -2321,6 +2309,19 @@ class Scop {
23212309
MinMaxAliasGroups.back().first = MinMaxAccessesReadWrite;
23222310
MinMaxAliasGroups.back().second = MinMaxAccessesReadOnly;
23232311
}
2312+
2313+
/// Remove statements from the list of scop statements.
2314+
///
2315+
/// @param ShouldDelete A function that returns true if the statement passed
2316+
/// to it should be deleted.
2317+
/// @param AfterHoisting If true, also remove from data access lists.
2318+
/// These lists are filled during
2319+
/// ScopBuilder::buildAccessRelations. Therefore, if this
2320+
/// method is called before buildAccessRelations, false
2321+
/// must be passed.
2322+
void removeStmts(std::function<bool(ScopStmt &)> ShouldDelete,
2323+
bool AfterHoisting = true);
2324+
23242325
/// Get an isl string representing the context.
23252326
std::string getContextStr() const;
23262327

polly/lib/Transform/Simplify.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ static int const SimplifyMaxDisjuncts = 4;
4141
TWO_STATISTICS(ScopsProcessed, "Number of SCoPs processed");
4242
TWO_STATISTICS(ScopsModified, "Number of SCoPs simplified");
4343

44+
TWO_STATISTICS(TotalEmptyDomainsRemoved,
45+
"Number of statement with empty domains removed in any SCoP");
4446
TWO_STATISTICS(TotalOverwritesRemoved, "Number of removed overwritten writes");
4547
TWO_STATISTICS(TotalWritesCoalesced, "Number of writes coalesced with another");
4648
TWO_STATISTICS(TotalRedundantWritesRemoved,
@@ -124,6 +126,9 @@ class Simplify : public ScopPass {
124126
/// The last/current SCoP that is/has been processed.
125127
Scop *S;
126128

129+
/// Number of statements with empty domains removed from the SCoP.
130+
int EmptyDomainsRemoved = 0;
131+
127132
/// Number of writes that are overwritten anyway.
128133
int OverwritesRemoved = 0;
129134

@@ -147,10 +152,36 @@ class Simplify : public ScopPass {
147152

148153
/// Return whether at least one simplification has been applied.
149154
bool isModified() const {
150-
return OverwritesRemoved > 0 || WritesCoalesced > 0 ||
151-
RedundantWritesRemoved > 0 || EmptyPartialAccessesRemoved > 0 ||
152-
DeadAccessesRemoved > 0 || DeadInstructionsRemoved > 0 ||
153-
StmtsRemoved > 0;
155+
return EmptyDomainsRemoved > 0 || OverwritesRemoved > 0 ||
156+
WritesCoalesced > 0 || RedundantWritesRemoved > 0 ||
157+
EmptyPartialAccessesRemoved > 0 || DeadAccessesRemoved > 0 ||
158+
DeadInstructionsRemoved > 0 || StmtsRemoved > 0;
159+
}
160+
161+
/// Remove statements that are never executed due to their domains being
162+
/// empty.
163+
///
164+
/// In contrast to Scop::simplifySCoP, this removes based on the SCoP's
165+
/// effective domain, i.e. including the SCoP's context as used by some other
166+
/// simplification methods in this pass. This is necessary because the
167+
/// analysis on empty domains is unreliable, e.g. remove a scalar value
168+
/// definition MemoryAccesses, but not its use.
169+
void removeEmptyDomainStmts() {
170+
size_t NumStmtsBefore = S->getSize();
171+
172+
auto ShouldDelete = [](ScopStmt &Stmt) -> bool {
173+
auto EffectiveDomain =
174+
Stmt.getDomain().intersect_params(Stmt.getParent()->getContext());
175+
return EffectiveDomain.is_empty();
176+
};
177+
S->removeStmts(ShouldDelete);
178+
179+
assert(NumStmtsBefore >= S->getSize());
180+
EmptyDomainsRemoved = NumStmtsBefore - S->getSize();
181+
LLVM_DEBUG(dbgs() << "Removed " << EmptyDomainsRemoved << " (of "
182+
<< NumStmtsBefore
183+
<< ") statements with empty domains \n");
184+
TotalEmptyDomainsRemoved[CallNo] += EmptyDomainsRemoved;
154185
}
155186

156187
/// Remove writes that are overwritten unconditionally later in the same
@@ -587,6 +618,8 @@ class Simplify : public ScopPass {
587618
/// Print simplification statistics to @p OS.
588619
void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const {
589620
OS.indent(Indent) << "Statistics {\n";
621+
OS.indent(Indent + 4) << "Empty domains removed: " << EmptyDomainsRemoved
622+
<< '\n';
590623
OS.indent(Indent + 4) << "Overwrites removed: " << OverwritesRemoved
591624
<< '\n';
592625
OS.indent(Indent + 4) << "Partial writes coalesced: " << WritesCoalesced
@@ -633,6 +666,9 @@ class Simplify : public ScopPass {
633666
this->S = &S;
634667
ScopsProcessed[CallNo]++;
635668

669+
LLVM_DEBUG(dbgs() << "Removing statements that are never executed...\n");
670+
removeEmptyDomainStmts();
671+
636672
LLVM_DEBUG(dbgs() << "Removing partial writes that never happen...\n");
637673
removeEmptyPartialAccesses();
638674

@@ -683,6 +719,7 @@ class Simplify : public ScopPass {
683719
virtual void releaseMemory() override {
684720
S = nullptr;
685721

722+
EmptyDomainsRemoved = 0;
686723
OverwritesRemoved = 0;
687724
WritesCoalesced = 0;
688725
RedundantWritesRemoved = 0;

polly/test/Simplify/func-b320a7.ll

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
; RUN: opt %loadPolly -polly-simplify -polly-optree -analyze < %s | FileCheck %s -match-full-lines
2+
3+
; llvm.org/PR47098
4+
; Use-after-free by reference to Stmt remaining in InstStmtMap after removing it has been removed by Scop::simplifyScop.
5+
; Removal happened in -polly-simplify, Reference was using in -polly-optree.
6+
; Check that Simplify removes the definition of %0 as well of its use.
7+
8+
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
9+
target triple = "x86_64-pc-windows-msvc19.26.28806"
10+
11+
@"?var_27@@3JA" = external dso_local local_unnamed_addr global i32, align 4
12+
@"?var_28@@3HA" = external dso_local local_unnamed_addr global i32, align 4
13+
14+
; Function Attrs: nofree norecurse nounwind uwtable
15+
define dso_local void @"?test@@YAXHEQEAY3M@1BI@BJ@H@Z"(i32 %a, i8 %b, [12 x [2 x [24 x [25 x i32]]]]* nocapture readonly %c) local_unnamed_addr {
16+
entry:
17+
br label %entry.split
18+
19+
entry.split: ; preds = %entry
20+
%sext = shl i32 %a, 16
21+
%conv4 = ashr exact i32 %sext, 16
22+
%sub = add nsw i32 %conv4, -25941
23+
%cmp535 = icmp sgt i32 %sext, 1700069376
24+
br i1 %cmp535, label %for.cond8.preheader.lr.ph, label %for.cond.cleanup.critedge
25+
26+
for.cond8.preheader.lr.ph: ; preds = %entry.split
27+
%conv10 = zext i8 %b to i64
28+
%sub11 = add nsw i64 %conv10, -129
29+
%cmp1232.not = icmp eq i64 %sub11, 0
30+
br i1 %cmp1232.not, label %for.cond8.preheader, label %for.cond8.preheader.us
31+
32+
for.cond8.preheader.us: ; preds = %for.cond8.preheader.lr.ph, %for.cond8.for.cond.cleanup13_crit_edge.us
33+
%e.036.us = phi i16 [ %add.us, %for.cond8.for.cond.cleanup13_crit_edge.us ], [ 0, %for.cond8.preheader.lr.ph ]
34+
%idxprom.us = sext i16 %e.036.us to i64
35+
br label %for.body14.us
36+
37+
for.body14.us: ; preds = %for.cond8.preheader.us, %for.body14.us
38+
%indvars.iv = phi i64 [ 0, %for.cond8.preheader.us ], [ %indvars.iv.next, %for.body14.us ]
39+
%arrayidx19.us = getelementptr inbounds [12 x [2 x [24 x [25 x i32]]]], [12 x [2 x [24 x [25 x i32]]]]* %c, i64 6, i64 2, i64 1, i64 %idxprom.us, i64 %indvars.iv
40+
%0 = load i32, i32* %arrayidx19.us, align 4, !tbaa !3
41+
store i32 0, i32* @"?var_28@@3HA", align 4, !tbaa !3
42+
%indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
43+
%exitcond.not = icmp eq i64 %indvars.iv.next, %sub11
44+
br i1 %exitcond.not, label %for.cond8.for.cond.cleanup13_crit_edge.us, label %for.body14.us
45+
46+
for.cond8.for.cond.cleanup13_crit_edge.us: ; preds = %for.body14.us
47+
%add.us = add i16 %e.036.us, 4
48+
%conv2.us = sext i16 %add.us to i32
49+
%cmp5.us = icmp sgt i32 %sub, %conv2.us
50+
br i1 %cmp5.us, label %for.cond8.preheader.us, label %for.cond.cleanup.critedge.loopexit38
51+
52+
for.cond.cleanup.critedge.loopexit38: ; preds = %for.cond8.for.cond.cleanup13_crit_edge.us
53+
store i32 %0, i32* @"?var_27@@3JA", align 4, !tbaa !7
54+
br label %for.cond.cleanup.critedge
55+
56+
for.cond.cleanup.critedge: ; preds = %for.cond8.preheader, %for.cond.cleanup.critedge.loopexit38, %entry.split
57+
ret void
58+
59+
for.cond8.preheader: ; preds = %for.cond8.preheader.lr.ph, %for.cond8.preheader
60+
%e.036 = phi i16 [ %add, %for.cond8.preheader ], [ 0, %for.cond8.preheader.lr.ph ]
61+
%add = add i16 %e.036, 4
62+
%conv2 = sext i16 %add to i32
63+
%cmp5 = icmp sgt i32 %sub, %conv2
64+
br i1 %cmp5, label %for.cond8.preheader, label %for.cond.cleanup.critedge
65+
}
66+
67+
!3 = !{!4, !4, i64 0}
68+
!4 = !{!"int", !5, i64 0}
69+
!5 = !{!"omnipotent char", !6, i64 0}
70+
!6 = !{!"Simple C++ TBAA"}
71+
!7 = !{!8, !8, i64 0}
72+
!8 = !{!"long", !5, i64 0}
73+
74+
75+
; CHECK: Statistics {
76+
; CHECK: Empty domains removed: 2
77+
; CHECK: }
78+
79+
; CHECK: After accesses {
80+
; CHECK-NOT: Stmt_for_body14_us_a
81+
; CHECK-NOT: Stmt_for_body14_us
82+
; CHECK: }

0 commit comments

Comments
 (0)