Skip to content

Commit 28be741

Browse files
author
Mandeep Singh Grang
authored
Use widened bounds to update bounds in context (#821)
Use the widened bounds calculated by the analysis in BoundsAnalysis.cpp to widen the bounds of nt_array_ptr.
1 parent d2f589f commit 28be741

File tree

5 files changed

+149
-38
lines changed

5 files changed

+149
-38
lines changed

clang/include/clang/Sema/BoundsAnalysis.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ namespace clang {
203203
// @param[in] B is the current CFGBlock.
204204
// return A mapping of Stmts to variables whose bounds are killed by the
205205
// Stmt.
206-
StmtDeclSetTy GetKillSet(const clang::CFGBlock *B);
206+
StmtDeclSetTy GetKilledBounds(const clang::CFGBlock *B);
207207

208208
private:
209209
// Compute Gen set for each edge in the CFG. If there is an edge B1->B2 and

clang/lib/Sema/BoundsAnalysis.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -720,13 +720,21 @@ void BoundsAnalysis::ComputeOutSets(ElevatedCFGBlock *EB,
720720
}
721721
}
722722

723-
StmtDeclSetTy BoundsAnalysis::GetKillSet(const CFGBlock *B) {
724-
ElevatedCFGBlock *EB = BlockMap[B];
723+
StmtDeclSetTy BoundsAnalysis::GetKilledBounds(const CFGBlock *B) {
724+
auto I = BlockMap.find(B);
725+
if (I == BlockMap.end())
726+
return StmtDeclSetTy();
727+
728+
ElevatedCFGBlock *EB = I->second;
725729
return EB->Kill;
726730
}
727731

728732
BoundsMapTy BoundsAnalysis::GetWidenedBounds(const CFGBlock *B) {
729-
ElevatedCFGBlock *EB = BlockMap[B];
733+
auto I = BlockMap.find(B);
734+
if (I == BlockMap.end())
735+
return BoundsMapTy();
736+
737+
ElevatedCFGBlock *EB = I->second;
730738
return EB->In;
731739
}
732740

clang/lib/Sema/SemaBounds.cpp

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,6 +2142,78 @@ namespace {
21422142
}
21432143
}
21442144

2145+
void ResetKilledBounds(StmtDeclSetTy &KilledBounds, Stmt *St,
2146+
BoundsContextTy &ObservedBounds) {
2147+
auto I = KilledBounds.find(St);
2148+
if (I == KilledBounds.end())
2149+
return;
2150+
2151+
// KilledBounds stores a mapping of statements to all variables whose
2152+
// bounds are killed by each statement. Here we reset the bounds of all
2153+
// variables killed by the statement S to the declared bounds.
2154+
for (const VarDecl *V : I->second) {
2155+
if (const BoundsExpr *Bounds = V->getBoundsExpr())
2156+
2157+
// TODO: Throughout clang in general (and inside dataflow analysis in
2158+
// particular) we repeatedly invoke ExpandBoundsToRange in order to
2159+
// canonicalize the bounds of a variable to RangeBoundsExpr. Sometimes
2160+
// we do this multiple times for the same variable. This is very
2161+
// inefficient because ExpandBoundsToRange can allocate AST data
2162+
// structures that are permanently allocated and increase the memory
2163+
// usage of the compiler. The solution is to canonicalize the bounds
2164+
// once and attach it to the VarDecl. See issue
2165+
// https://github.com/microsoft/checkedc-clang/issues/830.
2166+
2167+
ObservedBounds[V] = S.ExpandBoundsToRange(V, Bounds);
2168+
}
2169+
}
2170+
2171+
void UpdateCtxWithWidenedBounds(BoundsMapTy &WidenedBounds,
2172+
BoundsContextTy &ObservedBounds) {
2173+
// WidenedBounds contains the mapping from _Nt_array_ptr to the offset by
2174+
// which its declared bounds should be widened. In this function we apply
2175+
// the offset to the declared bounds of the _Nt_array_ptr and update its
2176+
// bounds in ObservedBounds.
2177+
2178+
for (const auto item : WidenedBounds) {
2179+
const VarDecl *V = item.first;
2180+
unsigned Offset = item.second;
2181+
2182+
// We normalize the declared bounds to RangBoundsExpr here so that we
2183+
// can easily apply the offset to the upper bound.
2184+
2185+
// TODO: Throughout clang in general (and inside dataflow analysis in
2186+
// particular) we repeatedly invoke ExpandBoundsToRange in order to
2187+
// canonicalize the bounds of a variable to RangeBoundsExpr. Sometimes
2188+
// we do this multiple times for the same variable. This is very
2189+
// inefficient because ExpandBoundsToRange can allocate AST data
2190+
// structures that are permanently allocated and increase the memory
2191+
// usage of the compiler. The solution is to canonicalize the bounds
2192+
// once and attach it to the VarDecl. See issue
2193+
// https://github.com/microsoft/checkedc-clang/issues/830.
2194+
2195+
BoundsExpr *Bounds = S.ExpandBoundsToRange(V, V->getBoundsExpr());
2196+
if (RangeBoundsExpr *RBE = dyn_cast<RangeBoundsExpr>(Bounds)) {
2197+
const llvm::APInt
2198+
APIntOff(Context.getTargetInfo().getPointerWidth(0), Offset);
2199+
IntegerLiteral *WidenedOffset = CreateIntegerLiteral(APIntOff);
2200+
2201+
Expr *Lower = RBE->getLowerExpr();
2202+
Expr *Upper = RBE->getUpperExpr();
2203+
2204+
// WidenedUpperBound = UpperBound + WidenedOffset.
2205+
Expr *WidenedUpper = ExprCreatorUtil::CreateBinaryOperator(
2206+
S, Upper, WidenedOffset,
2207+
BinaryOperatorKind::BO_Add);
2208+
2209+
RangeBoundsExpr *R =
2210+
new (Context) RangeBoundsExpr(Lower, WidenedUpper,
2211+
SourceLocation(), SourceLocation());
2212+
ObservedBounds[V] = R;
2213+
}
2214+
}
2215+
}
2216+
21452217
// Walk the CFG, traversing basic blocks in reverse post-oder.
21462218
// For each element of a block, check bounds declarations. Skip
21472219
// CFG elements that are subexpressions of other CFG elements.
@@ -2177,13 +2249,28 @@ namespace {
21772249
StmtSet MemoryCheckedStmts;
21782250
StmtSet BoundsCheckedStmts;
21792251
IdentifyChecked(Body, MemoryCheckedStmts, BoundsCheckedStmts, CheckedScopeSpecifier::CSS_Unchecked);
2252+
2253+
// Run the bounds widening analysis on this function.
2254+
BoundsAnalysis BA = getBoundsAnalyzer();
2255+
BA.WidenBounds(FD);
2256+
if (S.getLangOpts().DumpWidenedBounds)
2257+
BA.DumpWidenedBounds(FD);
2258+
21802259
PostOrderCFGView POView = PostOrderCFGView(Cfg);
21812260
ResetFacts();
21822261
for (const CFGBlock *Block : POView) {
21832262
AFA.GetFacts(Facts);
21842263
CheckingState BlockState = GetIncomingBlockState(Block, BlockStates);
2185-
// TODO: update BlockState.ObservedBounds to reflect the widened bounds
2186-
// for the block.
2264+
2265+
// Get the widened bounds for the current block as computed by the
2266+
// bounds widening analysis invoked by WidenBounds above.
2267+
BoundsMapTy WidenedBounds = BA.GetWidenedBounds(Block);
2268+
// Also get the bounds killed (if any) by each statement in the current
2269+
// block.
2270+
StmtDeclSetTy KilledBounds = BA.GetKilledBounds(Block);
2271+
// Update the Observed bounds with the widened bounds calculated above.
2272+
UpdateCtxWithWidenedBounds(WidenedBounds, BlockState.ObservedBounds);
2273+
21872274
for (CFGElement Elem : *Block) {
21882275
if (Elem.getKind() == CFGElement::Statement) {
21892276
CFGStmt CS = Elem.castAs<CFGStmt>();
@@ -2218,8 +2305,11 @@ namespace {
22182305
// bounds for each variable v that is in scope are the widened
22192306
// bounds for v (if any), or the declared bounds for v (if any).
22202307
GetDeclaredBounds(this->S, BlockState.ObservedBounds, S);
2221-
// TODO: update BlockState.ObservedBounds to reset any widened
2222-
// bounds that are killed by S to the declared variable bounds.
2308+
2309+
// If any bounds are killed by statement S, reset their bounds
2310+
// to their declared bounds.
2311+
ResetKilledBounds(KilledBounds, S, BlockState.ObservedBounds);
2312+
22232313
BoundsContextTy InitialObservedBounds = BlockState.ObservedBounds;
22242314
BlockState.G.clear();
22252315

@@ -5191,13 +5281,6 @@ void Sema::CheckFunctionBodyBoundsDecls(FunctionDecl *FD, Stmt *Body) {
51915281
Checker.Check(Body, CheckedScopeSpecifier::CSS_Unchecked);
51925282
}
51935283

5194-
if (Cfg != nullptr) {
5195-
BoundsAnalysis BA = Checker.getBoundsAnalyzer();
5196-
BA.WidenBounds(FD);
5197-
if (getLangOpts().DumpWidenedBounds)
5198-
BA.DumpWidenedBounds(FD);
5199-
}
5200-
52015284
#if TRACE_CFG
52025285
llvm::outs() << "Done " << FD->getName() << "\n";
52035286
#endif
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Tests for using bounds widening to control "out-of-bounds access" errors.
2+
//
3+
// RUN: %clang_cc1 -verify -verify-ignore-unexpected=note -fcheckedc-extension %s
4+
5+
#include <limits.h>
6+
7+
void f1() {
8+
_Nt_array_ptr<char> p : bounds(p, p) = "";
9+
10+
if (*p)
11+
if (*(p + 1))
12+
if (*(p + 3)) // expected-error {{out-of-bounds memory access}}
13+
{}
14+
15+
if (*p) {
16+
p++;
17+
if (*(p + 1)) // expected-error {{out-of-bounds memory access}}
18+
{}
19+
}
20+
}

clang/test/CheckedC/inferred-bounds/widened-bounds.c

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ void f2() {
2121
_Nt_array_ptr<char> p : count(0) = "ab";
2222

2323
if (*p)
24-
if (*(p + 1)) // expected-error {{out-of-bounds memory access}}
25-
if (*(p + 2)) // expected-error {{out-of-bounds memory access}}
24+
if (*(p + 1))
25+
if (*(p + 2))
2626
{}
2727

2828
// CHECK: In function: f2
@@ -84,8 +84,8 @@ void f5() {
8484
char p _Nt_checked[] : count(0) = "abc";
8585

8686
if (p[0])
87-
if (p[1]) // expected-error {{out-of-bounds memory access}}
88-
if (p[2]) // expected-error {{out-of-bounds memory access}}
87+
if (p[1])
88+
if (p[2])
8989
{}
9090

9191
// CHECK: In function: f5
@@ -149,7 +149,7 @@ void f8() {
149149
if (*p)
150150
if (*(p + 1))
151151
if (*(p + 2))
152-
if (*(p + 3)) // expected-error {{out-of-bounds memory access}}
152+
if (*(p + 3))
153153
{}
154154

155155
// CHECK: In function: f8
@@ -267,8 +267,8 @@ void f13() {
267267

268268
if (p[0])
269269
if (1[p])
270-
if (p[2]) // expected-error {{out-of-bounds memory access}}
271-
if (3[p]) // expected-error {{out-of-bounds memory access}}
270+
if (p[2])
271+
if (3[p])
272272
{}
273273

274274
// CHECK: In function: f13
@@ -319,7 +319,7 @@ void f15(int i) {
319319

320320
_Nt_array_ptr<char> q : count(0) = "a";
321321
if (*q)
322-
if (*(q - 1)) // expected-error {{out-of-bounds memory access}}
322+
if (*(q - 1))
323323
{}
324324

325325
// CHECK: [B7]
@@ -355,7 +355,7 @@ void f16(_Nt_array_ptr<char> p : bounds(p, p)) {
355355
_Nt_array_ptr<char> r : bounds(p, p + 1) = "a";
356356

357357
if (*(p))
358-
if (*(p + 1)) // expected-error {{out-of-bounds memory access}}
358+
if (*(p + 1))
359359
{}
360360

361361
// CHECK: In function: f16
@@ -447,9 +447,9 @@ void f19() {
447447
_Nt_array_ptr<char> p : count(0) = "a";
448448

449449
if (*p)
450-
if (*(p + 1)) // expected-error {{out-of-bounds memory access}}
451-
if (*(p + 3)) // expected-error {{out-of-bounds memory access}}
452-
if (*(p + 2)) // expected-error {{out-of-bounds memory access}}
450+
if (*(p + 1))
451+
if (*(p + 3))
452+
if (*(p + 2))
453453
{}
454454

455455
// CHECK: In function: f19
@@ -540,8 +540,8 @@ void f21() {
540540
char p _Nt_checked[] : count(0) = "abc";
541541

542542
while (p[0])
543-
while (p[1]) // expected-error {{out-of-bounds memory access}}
544-
while (p[2]) // expected-error {{out-of-bounds memory access}}
543+
while (p[1])
544+
while (p[2])
545545
{}
546546

547547
// CHECK: In function: f21
@@ -565,8 +565,8 @@ void f22() {
565565
_Nt_array_ptr<char> p : count(0) = "a";
566566

567567
if (*p)
568-
while (*(p + 1)) // expected-error {{out-of-bounds memory access}}
569-
if (*(p + 2)) // expected-error {{out-of-bounds memory access}}
568+
while (*(p + 1))
569+
if (*(p + 2))
570570
{}
571571

572572
// CHECK: In function: f22
@@ -615,7 +615,7 @@ B: p;
615615

616616
while (*p) {
617617
p;
618-
while (*(p + 1)) { // expected-error {{out-of-bounds memory access}}
618+
while (*(p + 1)) {
619619
C: p;
620620
}
621621
}
@@ -674,9 +674,9 @@ void f25() {
674674

675675
for (; *p; ) {
676676
i = 0;
677-
for (; *(p + 1); ) { // expected-error {{out-of-bounds memory access}}
677+
for (; *(p + 1); ) {
678678
i = 1;
679-
for (; *(p + 2); ) { // expected-error {{out-of-bounds memory access}}
679+
for (; *(p + 2); ) {
680680
i = 2;
681681
}
682682
}
@@ -874,7 +874,7 @@ void f28() {
874874

875875
switch (*p) {
876876
case 1:
877-
switch (*(p + 1)) { // expected-error {{out-of-bounds memory access}}
877+
switch (*(p + 1)) {
878878
case 2: break;
879879
}
880880
// CHECK: [B8]
@@ -917,17 +917,17 @@ void f29() {
917917
// CHECK: case 'a':
918918
// CHECK: upper_bound(p) = 1
919919

920-
if (*(p + 1)) { // expected-error {{out-of-bounds memory access}}
920+
if (*(p + 1)) {
921921
i = 0;
922922
// CHECK: 1: i = 0
923923
// CHECK: upper_bound(p) = 2
924924

925-
for (;*(p + 2);) { // expected-error {{out-of-bounds memory access}}
925+
for (;*(p + 2);) {
926926
i = 1;
927927
// CHECK: 1: i = 1
928928
// CHECK: upper_bound(p) = 3
929929

930-
while (*(p + 3)) { // expected-error {{out-of-bounds memory access}}
930+
while (*(p + 3)) {
931931
i = 2;
932932
// CHECK: 1: i = 2
933933
// CHECK: upper_bound(p) = 4

0 commit comments

Comments
 (0)