diff --git a/clang/include/clang/Sema/BoundsWideningAnalysis.h b/clang/include/clang/Sema/BoundsWideningAnalysis.h index 5d893b55293d..d304306499a3 100644 --- a/clang/include/clang/Sema/BoundsWideningAnalysis.h +++ b/clang/include/clang/Sema/BoundsWideningAnalysis.h @@ -18,6 +18,7 @@ #include "clang/AST/ExprUtils.h" #include "clang/AST/PrettyPrinter.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Sema/BoundsUtils.h" #include "clang/Sema/CheckedCAnalysesPrepass.h" #include "clang/Sema/Sema.h" @@ -54,6 +55,14 @@ namespace clang { // for printing the blocks in a deterministic order. using OrderedBlocksTy = std::vector; + // A tuple (Tup) of three elements such that we need to replace Tup[0] with + // Tup[1] in the bounds of every pointer in Tup[3]. + using LValuesToReplaceInBoundsTy = std::tuple; + + // A mapping of invertible statements to LValuesToReplaceInBoundsTy. + using InvertibleStmtMapTy = llvm::DenseMap; + } // end namespace clang namespace clang { @@ -128,20 +137,20 @@ namespace clang { // Get the set of variables that are pointers to null-terminated arrays and // in whose lower bounds expressions the variables in Vars occur. // @param[in] Vars is a set of variables. - // @param[out] NullTermPtrsWithVarsInLowerBounds is a set of variables that - // are pointers to null-terminated arrays and in whose lower bounds - // expressions the variables in Vars occur. - void GetNullTermPtrsWithVarsInLowerBounds( - VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInLowerBounds) const; + // @param[out] PtrsWithVarsInLowerBounds is a set of variables that are + // pointers to null-terminated arrays and in whose lower bounds expressions + // the variables in Vars occur. + void GetPtrsWithVarsInLowerBounds( + VarSetTy &Vars, VarSetTy &PtrsWithVarsInLowerBounds) const; // Get the set of variables that are pointers to null-terminated arrays and // in whose upper bounds expressions the variables in Vars occur. // @param[in] Vars is a set of variables. - // @param[out] NullTermPtrsWithVarsInLowerBounds is a set of variables that - // are pointers to null-terminated arrays and in whose upper bounds - // expressions the variables in Vars occur. - void GetNullTermPtrsWithVarsInUpperBounds( - VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInUpperBounds) const; + // @param[out] PtrsWithVarsInLowerBounds is a set of variables that are + // pointers to null-terminated arrays and in whose upper bounds expressions + // the variables in Vars occur. + void GetPtrsWithVarsInUpperBounds( + VarSetTy &Vars, VarSetTy &PtrsWithVarsInUpperBounds) const; // Add an offset to a given expression. // @param[in] E is the given expression. @@ -287,6 +296,14 @@ namespace clang { // The Out set of the previous statement of a statement in a block. BoundsMapTy OutOfPrevStmt; + // This stores the adjusted bounds after we have determined the + // invertibility of the current statement that modifies variables + // occurring in bounds expressions. + StmtBoundsMapTy AdjustedBounds; + + // A mapping of invertible statements to LValuesToReplaceInBoundsTy. + InvertibleStmtMapTy InvertibleStmts; + ElevatedCFGBlock(const CFGBlock *B) : Block(B) {} }; // end of ElevatedCFGBlock class. @@ -492,6 +509,28 @@ namespace clang { const Stmt *CurrStmt, BoundsMapTy &VarsAndBounds); + // Check if CurrStmt is invertible w.r.t. the variables modified by + // CurrStmt. + // Note: This function modifies the set EB->InvertibleStmts. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] CurrStmt is the current statement. + // @param[in] PtrsWithAffectedBounds is the set of variables that are + // pointers to null-terminated arrays whose bounds are affected by + // modification to variables that occur in their bounds expressions by + // CurrStmt. + void CheckStmtInvertibility(ElevatedCFGBlock *EB, + const Stmt *CurrStmt, + VarSetTy PtrsWithAffectedBounds) const; + + // Update the bounds in StmtOut with the adjusted bounds for the current + // statement, if they exist. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] CurrStmt is the current statement. + // @param[out] StmtOut is updated with the adjusted bounds for CurrStmt, if + // they exist. + void UpdateAdjustedBounds(ElevatedCFGBlock *EB, const Stmt *CurrStmt, + BoundsMapTy &StmtOut) const; + // Order the blocks by block number to get a deterministic iteration order // for the blocks. // @return Blocks ordered by block number from higher to lower since block diff --git a/clang/lib/Sema/BoundsWideningAnalysis.cpp b/clang/lib/Sema/BoundsWideningAnalysis.cpp index e2c21d61701d..b70096730e2f 100644 --- a/clang/lib/Sema/BoundsWideningAnalysis.cpp +++ b/clang/lib/Sema/BoundsWideningAnalysis.cpp @@ -93,17 +93,90 @@ BoundsMapTy BoundsWideningAnalysis::GetOutOfLastStmt( if (!CurrStmt) continue; + // The In of the current statement is the value of StmtOut computed so far. + BoundsMapTy InOfCurrStmt = StmtOut; + // If this is the last statement of the current block, then at this point - // StmtOut contains the Out set of the second last statement of the block. - // This is equal to the In set for the last statement of this block. So we - // set InOfLastStmt to StmtOut. + // InOfCurrStmt contains the Out set of the second last statement of the + // block. This is equal to the In set for the last statement of this + // block. So we set InOfLastStmt to InOfCurrStmt. if (CurrStmt == EB->LastStmt) - EB->InOfLastStmt = StmtOut; - - auto Diff = BWUtil.Difference(StmtOut, EB->StmtKill[CurrStmt]); + EB->InOfLastStmt = InOfCurrStmt; - // TODO: Update StmtOut based on the invertibility of CurrStmt. + // StmtOut = (InOfCurrStmt - StmtKill) u StmtGen. + auto Diff = BWUtil.Difference(InOfCurrStmt, EB->StmtKill[CurrStmt]); StmtOut = BWUtil.Union(Diff, EB->StmtGen[CurrStmt]); + + // Update StmtOut based on the invertibility of CurrStmt. + auto InvStmtIt = EB->InvertibleStmts.find(CurrStmt); + if (InvStmtIt == EB->InvertibleStmts.end()) + continue; + + // At CurrStmt, we need to replace the ModifiedLValue with the + // OriginalLValue in the bounds of every null-terminated array occurring in + // PtrsWithAffectedBounds. + auto ValuesToReplaceInBounds = InvStmtIt->second; + + Expr *ModifiedLValue = std::get<0>(ValuesToReplaceInBounds); + Expr *OriginalLValue = std::get<1>(ValuesToReplaceInBounds); + VarSetTy PtrsWithAffectedBounds = std::get<2>(ValuesToReplaceInBounds); + + // TODO: Determine the Checked scope for each statement. + CheckedScopeSpecifier CSS = CheckedScopeSpecifier::CSS_None; + + for (const VarDecl *V : PtrsWithAffectedBounds) { + auto StmtInIt = InOfCurrStmt.find(V); + if (StmtInIt == InOfCurrStmt.end()) + continue; + + BoundsExpr *SrcBounds = StmtInIt->second; + + // Replace the modified LValue with the original LValue in the bounds + // expression of V. + BoundsExpr *AdjustedBounds = + BoundsUtil::ReplaceLValueInBounds(SemaRef, SrcBounds, ModifiedLValue, + OriginalLValue, CSS); + RangeBoundsExpr *AdjustedRangeBounds = + dyn_cast_or_null(AdjustedBounds); + + if (!AdjustedRangeBounds) + llvm_unreachable("Invalid RangeBoundsExpr!"); + + // In the bounds widening analysis the widest value of an upper bounds + // expression is Top, whereas the narrowest value is the declared upper + // bound. This means that the upper bound can/should never become + // narrower than the declared upper bound. + // So in case we have an invertible statement that modifies a variable + // occurring in the bounds expression of a null-terminated array we + // should always reset the bounds to the declared upper bound except when + // replacement of the modified LValue with its original LValue results in + // a bounds expression which is strictly wider than the declared upper + // bound. + // So we will proceed only if AdjustedRangeBounds is wider than + // StmtOut[V] which contains the declared bounds of V at this point. For + // example: + + // Let DB = Declared bounds, AB = Adjusted bounds. + + // DB = (p, p + len), AB = (p, p + len + 1) + // ==> AB is wider than DB ==> set bounds of V to AB. + + // DB = (p, p + len), AB = (p, p + len - 1) + // ==> AB is not wider than DB ==> set bounds of V to DB. + + // DB = (p, p + len), AB = (p, p + len + i) + // ==> AB cannot be compared to DB ==> set bounds of V to DB. + + if (!BWUtil.IsSubRange(AdjustedRangeBounds, StmtOut[V])) + continue; + + // Update the bounds of V with the adjusted bounds. + StmtOut[V] = AdjustedRangeBounds; + + // Store the adjusted bounds for the current statement. We will use these + // when clients invoke GetStmtIn or GetStmtOut. + EB->AdjustedBounds[CurrStmt][V] = AdjustedRangeBounds; + } } return StmtOut; } @@ -247,7 +320,7 @@ bool BoundsWideningAnalysis::ComputeInSet(ElevatedCFGBlock *EB) { } // Return true if the In set has changed, false otherwise. - return !BWUtil.IsEqual(EB->In, OrigIn); + return !BWUtil.IsEqual(OrigIn, EB->In); } BoundsMapTy BoundsWideningAnalysis::PruneOutSet( @@ -444,7 +517,7 @@ bool BoundsWideningAnalysis::ComputeOutSet(ElevatedCFGBlock *EB) { EB->Out = GetOutOfLastStmt(EB); // Return true if the Out set has changed, false otherwise. - return !BWUtil.IsEqual(EB->Out, OrigOut); + return !BWUtil.IsEqual(OrigOut, EB->Out); } void BoundsWideningAnalysis::InitBlockInOutSets(FunctionDecl *FD, @@ -511,15 +584,37 @@ BoundsMapTy BoundsWideningAnalysis::GetStmtOut(const CFGBlock *B, ElevatedCFGBlock *EB = BlockIt->second; - if (CurrStmt) { - auto Diff = BWUtil.Difference(EB->OutOfPrevStmt, EB->StmtKill[CurrStmt]); - auto StmtOut = BWUtil.Union(Diff, EB->StmtGen[CurrStmt]); - EB->OutOfPrevStmt = StmtOut; - return StmtOut; + // CurrStmt will be null if: + // 1. This method is called with a null value for CurrStmt, or + // 2. GetStmtIn calls this method to get the In set for the first statement + // of the block. Because the Out of the previous statement is equal to the In + // of the current statement, GetStmtIn will call this function with the + // previous statement of the first statment (which would be null). + + // In both cases we will set the OutOfPrevStmt to the In set of the block and + // return it. + if (!CurrStmt) { + EB->OutOfPrevStmt = EB->In; + return EB->In; } - EB->OutOfPrevStmt = EB->In; - return EB->In; + // If we are here it means the client wants the Out set for the first + // statement of the block (that is the reason PrevStmtMap[CurrStmt] is null). + // In this case, we set OutOfPrevStmt to the In set of the block and then + // apply the regular (In - Kill) u Gen computation on it. + if (!EB->PrevStmtMap[CurrStmt]) + EB->OutOfPrevStmt = EB->In; + + auto Diff = BWUtil.Difference(EB->OutOfPrevStmt, EB->StmtKill[CurrStmt]); + auto StmtOut = BWUtil.Union(Diff, EB->StmtGen[CurrStmt]); + + // Account for bounds which are killed by the current statement but which may + // have been adjusted using invertibility of the statement. This function + // modifies StmtOut. + UpdateAdjustedBounds(EB, CurrStmt, StmtOut); + + EB->OutOfPrevStmt = StmtOut; + return StmtOut; } BoundsMapTy BoundsWideningAnalysis::GetStmtIn(const CFGBlock *B, @@ -549,7 +644,14 @@ BoundsMapTy BoundsWideningAnalysis::GetBoundsWidenedAndNotKilled( ElevatedCFGBlock *EB = BlockIt->second; BoundsMapTy InOfCurrStmt = GetStmtIn(B, CurrStmt); - return BWUtil.Difference(InOfCurrStmt, EB->StmtKill[CurrStmt]); + auto BoundsWidenedAndNotKilled = BWUtil.Difference(InOfCurrStmt, + EB->StmtKill[CurrStmt]); + + // Account for bounds which are killed by the current statement but which may + // have been adjusted using invertibility of the statement. This function + // modifies BoundsWidenedAndNotKilled. + UpdateAdjustedBounds(EB, CurrStmt, BoundsWidenedAndNotKilled); + return BoundsWidenedAndNotKilled; } void BoundsWideningAnalysis::InitNullTermPtrsInFunc(FunctionDecl *FD) { @@ -659,20 +761,19 @@ void BoundsWideningAnalysis::GetVarsAndBoundsInPtrDeref( // _Nt_array_ptr s : bounds(p, s); // On a dereference expression like "*(p + i + j + 1)" - // GetNullTermPtrsWithVarsInUpperBounds() will return {p, q, r} because p + // GetPtrsWithVarsInUpperBounds() will return {p, q, r} because p // occurs in the upper bounds expressions of p, q and r. VarSetTy Vars; Vars.insert(NullTermPtrInExpr); - VarSetTy NullTermPtrsWithVarsInUpperBounds; - BWUtil.GetNullTermPtrsWithVarsInUpperBounds( - Vars, NullTermPtrsWithVarsInUpperBounds); + VarSetTy PtrsWithAffectedBounds; + BWUtil.GetPtrsWithVarsInUpperBounds(Vars, PtrsWithAffectedBounds); - // Now, the bounds of all variables in NullTermPtrsWithVarsInUpperBounds can - // potentially be widened to bounds(lower, DerefExpr + 1). + // Now, the bounds of all variables in PtrsWithAffectedBounds can potentially + // be widened to bounds(lower, DerefExpr + 1). - for (const VarDecl *V : NullTermPtrsWithVarsInUpperBounds) { + for (const VarDecl *V : PtrsWithAffectedBounds) { BoundsExpr *NormalizedBounds = SemaRef.NormalizeBounds(V); RangeBoundsExpr *R = dyn_cast_or_null(NormalizedBounds); @@ -700,18 +801,126 @@ void BoundsWideningAnalysis::GetVarsAndBoundsForModifiedVars( // Get the set of variables that are pointers to null-terminated arrays and // in whose lower and upper bounds expressions the modified variables occur. - VarSetTy NullTermPtrsWithVarsInBounds; - BWUtil.GetNullTermPtrsWithVarsInLowerBounds(ModifiedVars, - NullTermPtrsWithVarsInBounds); - BWUtil.GetNullTermPtrsWithVarsInUpperBounds(ModifiedVars, - NullTermPtrsWithVarsInBounds); + VarSetTy PtrsWithAffectedBounds; + BWUtil.GetPtrsWithVarsInLowerBounds(ModifiedVars, + PtrsWithAffectedBounds); + BWUtil.GetPtrsWithVarsInUpperBounds(ModifiedVars, + PtrsWithAffectedBounds); // For each null-terminated array we need to reset the bounds to its declared // bounds. - for (const VarDecl *V : NullTermPtrsWithVarsInBounds) { + for (const VarDecl *V : PtrsWithAffectedBounds) { BoundsExpr *NormalizedBounds = SemaRef.NormalizeBounds(V); VarsAndBounds[V] = dyn_cast_or_null(NormalizedBounds); } + + // If the modification of a variable by the current statement affects the + // bounds of a null-terminated array, then check invertibility of the + // statement. If the statement is invertible then store the statement, the + // modified LValue, the original LValue and the set of null-terminated arrays + // whose bounds are affected by the statement. We will use this info in the + // computation of the Out sets of statements which will, in turn be used to + // compute the Out sets of blocks. + CheckStmtInvertibility(EB, CurrStmt, PtrsWithAffectedBounds); +} + +void BoundsWideningAnalysis::CheckStmtInvertibility(ElevatedCFGBlock *EB, + const Stmt *CurrStmt, VarSetTy PtrsWithAffectedBounds) const { + + // If the variables modified by the current statement do not affect the + // bounds of any null-terminated array we do not need to check statement + // invertibility. + if (PtrsWithAffectedBounds.size() == 0) + return; + + Expr *ModifiedLValue = nullptr; + Expr *ModifyingExpr = nullptr; + + // If the current statement is a unary inc/dec. For example: ++len + if (const auto *UO = dyn_cast(CurrStmt)) { + if (!UO->isIncrementDecrementOp()) + return; + + // Get the LValue being incremented/decremented. For example: len + ModifiedLValue = UO->getSubExpr(); + if (!ModifiedLValue) + return; + + // Normalize the inc/dec of the LValue to LValue +/- 1. + // For example: ++len is normalized to len + 1 + // len-- is normalized to len - 1 + IntegerLiteral *One = ExprCreatorUtil::CreateIntegerLiteral( + Ctx, 1, ModifiedLValue->getType()); + + BinaryOperatorKind OpKind = UnaryOperator::isIncrementOp(UO->getOpcode()) ? + BO_Add : BO_Sub; + + // Here ModifyingExpr will be of the form len +/- 1. + ModifyingExpr = + ExprCreatorUtil::CreateBinaryOperator(SemaRef, ModifiedLValue, + One, OpKind); + + // Else if the current statement is an assignment statement. For example: + // len = e1 + } else if (const auto *BO = dyn_cast(CurrStmt)) { + if (!BO->isAssignmentOp()) + return; + + // ModifiedLValue is len. + ModifiedLValue = BO->getLHS(); + // ModifyingExpr is e1. + ModifyingExpr = BO->getRHS(); + + BinaryOperatorKind OpKind = BO->getOpcode(); + // If the current statement is of the form len += e1. + if (OpKind == BO_AddAssign || OpKind == BO_SubAssign) { + OpKind = OpKind == BO_AddAssign ? BO_Add : BO_Sub; + + // Normalize the ModifyingExpr to len + e1. + ModifyingExpr = + ExprCreatorUtil::CreateBinaryOperator(SemaRef, ModifiedLValue, + ModifyingExpr, OpKind); + } + } + + if (!ModifiedLValue || !ModifyingExpr) + return; + + CastExpr *Target = + ExprCreatorUtil::CreateImplicitCast(SemaRef, ModifiedLValue, + CK_LValueToRValue, + ModifiedLValue->getType()); + + // Check if the modifying expr is invertible w.r.t. the modified LValue. + if (InverseUtil::IsInvertible(SemaRef, ModifiedLValue, ModifyingExpr)) { + // Get the original LValue for the modified LValue. For example, for len++ + // the original LValue would be len - 1. + Expr *OriginalLValue = InverseUtil::Inverse(SemaRef, ModifiedLValue, + Target, ModifyingExpr); + + // Store the modified LValue, the original LValue and the set of + // null-terminated arrays whose bounds expressions are affected by the + // LValue being modified. + if (OriginalLValue) + EB->InvertibleStmts[CurrStmt] = std::make_tuple(ModifiedLValue, + OriginalLValue, + PtrsWithAffectedBounds); + } +} + +void BoundsWideningAnalysis::UpdateAdjustedBounds( + ElevatedCFGBlock *EB, const Stmt *CurrStmt, BoundsMapTy &StmtOut) const { + + auto AdjBoundsIt = EB->AdjustedBounds.find(CurrStmt); + if (AdjBoundsIt == EB->AdjustedBounds.end()) + return; + + for (auto Item : AdjBoundsIt->second) { + const VarDecl *V = Item.first; + RangeBoundsExpr *AdjustedBounds = Item.second; + + StmtOut[V] = AdjustedBounds; + } } void BoundsWideningAnalysis::PrintVarSet(VarSetTy VarSet, @@ -839,6 +1048,11 @@ void BoundsWideningAnalysis::DumpWidenedBounds(FunctionDecl *FD, PrintBoundsMap(EB->Out, PrintOption); } + if (CurrBlock->empty()) { + OS << "\n"; + continue; + } + for (CFGElement Elem : *CurrBlock) { if (Elem.getKind() != CFGElement::Statement) continue; @@ -1078,8 +1292,8 @@ void BoundsWideningUtil::GetModifiedVars(const Stmt *CurrStmt, GetModifiedVars(NestedStmt, ModifiedVars); } -void BoundsWideningUtil::GetNullTermPtrsWithVarsInLowerBounds( - VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInLowerBounds) const { +void BoundsWideningUtil::GetPtrsWithVarsInLowerBounds( + VarSetTy &Vars, VarSetTy &PtrsWithVarsInLowerBounds) const { // Get the set of variables that are pointers to null-terminated arrays and // in whose lower bounds expressions the variables in Vars occur. @@ -1089,13 +1303,13 @@ void BoundsWideningUtil::GetNullTermPtrsWithVarsInLowerBounds( if (VarPtrIt != BoundsVarsLower.end()) { for (const VarDecl *Ptr : VarPtrIt->second) if (!Ptr->isInvalidDecl() && IsNtArrayType(Ptr)) - NullTermPtrsWithVarsInLowerBounds.insert(Ptr); + PtrsWithVarsInLowerBounds.insert(Ptr); } } } -void BoundsWideningUtil::GetNullTermPtrsWithVarsInUpperBounds( - VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInUpperBounds) const { +void BoundsWideningUtil::GetPtrsWithVarsInUpperBounds( + VarSetTy &Vars, VarSetTy &PtrsWithVarsInUpperBounds) const { // Get the set of variables that are pointers to null-terminated arrays and // in whose upper bounds expressions the variables in Vars occur. @@ -1105,7 +1319,7 @@ void BoundsWideningUtil::GetNullTermPtrsWithVarsInUpperBounds( if (VarPtrIt != BoundsVarsUpper.end()) { for (const VarDecl *Ptr : VarPtrIt->second) if (!Ptr->isInvalidDecl() && IsNtArrayType(Ptr)) - NullTermPtrsWithVarsInUpperBounds.insert(Ptr); + PtrsWithVarsInUpperBounds.insert(Ptr); } } } diff --git a/clang/test/CheckedC/inferred-bounds/bounds-context.c b/clang/test/CheckedC/inferred-bounds/bounds-context.c index db0ec1fbdf2d..bcbc674c9402 100644 --- a/clang/test/CheckedC/inferred-bounds/bounds-context.c +++ b/clang/test/CheckedC/inferred-bounds/bounds-context.c @@ -300,7 +300,7 @@ void assign1(array_ptr arr : count(1)) { // expected-note {{(expanded) decl // Assignment to a variable used in other variables' bounds void assign2( array_ptr a : count(len - 1), // expected-note {{(expanded) declared bounds are 'bounds(a, a + len - 1)'}} - char b nt_checked[0] : count(len), // expected-note {{(expanded) declared bounds are 'bounds(b, b + len)'}} + char b nt_checked[0] : count(len), unsigned len ) { // Observed bounds context before assignment: { a => bounds(a, a + len - 1), b => bounds(b, b + len) } @@ -308,8 +308,6 @@ void assign2( // Observed bounds context after assignment : { a => bounds(a, a + ((len + 3) - 1)), b => bounds(b, b + (len + 3)) } len = len - 3; // expected-warning {{cannot prove declared bounds for 'a' are valid after assignment}} \ // expected-note {{(expanded) inferred bounds are 'bounds(a, a + len + 3 - 1)'}} \ - // expected-warning {{cannot prove declared bounds for 'b' are valid after assignment}} \ - // expected-note {{(expanded) inferred bounds are 'bounds(b, b + len + 3)'}} // CHECK: Statement S: // CHECK-NEXT: BinaryOperator {{.*}} '=' // CHECK-NEXT: DeclRefExpr {{.*}} 'len' diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds-invertibility.c b/clang/test/CheckedC/inferred-bounds/widened-bounds-invertibility.c new file mode 100644 index 000000000000..22a33d076caa --- /dev/null +++ b/clang/test/CheckedC/inferred-bounds/widened-bounds-invertibility.c @@ -0,0 +1,209 @@ +// Tests for invertibility of statements in the datafow analysis for bounds +// widening of null-terminated arrays. +// +// RUN: %clang_cc1 -fdump-widened-bounds -verify \ +// RUN: -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s \ +// RUN: | FileCheck %s + +// expected-no-diagnostics + +#include +#include + +int a; + +void f1(_Nt_array_ptr p : count(len), unsigned len) { + + while (*(p + len)) { + len++; + a = 1; + } + +// CHECK: Function: f1 +// CHECK: Block: B25 (Entry), Pred: Succ: B24 + +// CHECK: Block: B24, Pred: B22, B25, Succ: B23, B21 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B23, Pred: B24, Succ: B22 +// CHECK: Widened bounds before stmt: len++ +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + len - 1U + 1) + + while (*(p + len)) { + ++len; + a = 2; + } + +// CHECK: Block: B22, Pred: B23, Succ: B24 + +// CHECK: Block: B21, Pred: B19, B24, Succ: B20, B18 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B20, Pred: B21, Succ: B19 +// CHECK: Widened bounds before stmt: ++len +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + len - 1U + 1) + + while (*(p + len)) { + len--; + a = 3; + } + +// CHECK: Block: B19, Pred: B20, Succ: B21 + +// CHECK: Block: B18, Pred: B16, B21, Succ: B17, B15 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B17, Pred: B18, Succ: B16 +// CHECK: Widened bounds before stmt: len-- +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + len + 1U + 1) + + while (*(p + len)) { + --len; + a = 4; + } + +// CHECK: Block: B16, Pred: B17, Succ: B18 + +// CHECK: Block: B15, Pred: B13, B18, Succ: B14, B12 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B14, Pred: B15, Succ: B13 +// CHECK: Widened bounds before stmt: --len +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + len + 1U + 1) + + while (*(p + len)) { + len = len + 1; + a = 5; + } + +// CHECK: Block: B13, Pred: B14, Succ: B15 + +// CHECK: Block: B12, Pred: B10, B15, Succ: B11, B9 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B11, Pred: B12, Succ: B10 +// CHECK: Widened bounds before stmt: len = len + 1 +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + len - 1 + 1) + + while (*(p + len)) { + len = len - 1; + a = 6; + } + +// CHECK: Block: B10, Pred: B11, Succ: B12 + +// CHECK: Block: B9, Pred: B7, B12, Succ: B8, B6 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B8, Pred: B9, Succ: B7 +// CHECK: Widened bounds before stmt: len = len - 1 +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + len + 1 + 1) + + while (*(p + len)) { + len += 1; + a = 7; + } + +// CHECK: Block: B7, Pred: B8, Succ: B9 + +// CHECK: Block: B6, Pred: B4, B9, Succ: B5, B3 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B5, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: len += 1 +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + len - 1 + 1) + + while (*(p + len)) { + len -= 1; + a = 8; + } + +// CHECK: Block: B4, Pred: B5, Succ: B6 + +// CHECK: Block: B3, Pred: B1, B6, Succ: B2, B0 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: len -= 1 +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + len + 1 + 1) +} + +void f2(_Nt_array_ptr p : count(len), unsigned len) { + while (*(p + len)) { + if (*(p + len + 1)) { + if (*(p + len + 2)) { + len = len + 1; + a = *(p + len + 1); + } + } + } + +// CHECK: Function: f2 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B1, B6, Succ: B4, B0 +// CHECK: Widened bounds before stmt: *(p + len) +// CHECK: p: bounds(p, p + len) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + len + 1) +// CHECK: p: bounds(p, p + len + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + len + 2) +// CHECK: p: bounds(p, p + len + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: len = len + 1 +// CHECK: p: bounds(p, p + len + 2 + 1) + +// CHECK: Widened bounds before stmt: a = *(p + len + 1) +// CHECK: p: bounds(p, p + len - 1 + 2 + 1) +} + +void f3(_Nt_array_ptr p : count(len), unsigned len) { + len--; + a = 1; + +// CHECK: Function: f3 +// CHECK: Block: B2 (Entry), Pred: Succ: B1 + +// CHECK: Block: B1, Pred: B2, Succ: B0 +// CHECK: Widened bounds before stmt: len-- +// CHECK: p: bounds(p, p + len) + +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + len + 1U) +} diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds.c b/clang/test/CheckedC/inferred-bounds/widened-bounds.c index 914b8056956a..eef5b6ce3ee1 100644 --- a/clang/test/CheckedC/inferred-bounds/widened-bounds.c +++ b/clang/test/CheckedC/inferred-bounds/widened-bounds.c @@ -1540,7 +1540,7 @@ void f24() { while (*p) { p++; - while (*(p + 1)) { // expected-error {{out-of-bounds memory access}} + while (*(p + 1)) { a = 1; } } @@ -1550,7 +1550,7 @@ void f24() { // CHECK: Block: B8, Pred: B9, Succ: B7 // CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; -// CHECK: +// CHECK: // CHECK: Block: B7, Pred: B2, B8, Succ: B6, B1 // CHECK: Widened bounds before stmt: *p @@ -1562,11 +1562,11 @@ void f24() { // CHECK: Block: B5, Pred: B3, B6, Succ: B4, B2 // CHECK: Widened bounds before stmt: *(p + 1) -// CHECK: p: bounds(p, p + 0) +// CHECK: p: bounds(p - 1, p - 1 + 1) // CHECK: Block: B4, Pred: B5, Succ: B3 // CHECK: Widened bounds before stmt: a = 1 -// CHECK: p: bounds(p, p + 0) +// CHECK: p: bounds(p - 1, p - 1 + 1) // CHECK: Block: B3, Pred: B4, Succ: B5 @@ -1642,7 +1642,7 @@ void f25_1() { for (; *p; ) { p++; - for (; *(p + 1); ) { // expected-error {{out-of-bounds memory access}} + for (; *(p + 1); ) { a = 7; } } @@ -1657,11 +1657,11 @@ void f25_1() { // CHECK: Block: B5, Pred: B3, B6, Succ: B4, B2 // CHECK: Widened bounds before stmt: *(p + 1) -// CHECK: p: bounds(p, p + 0) +// CHECK: p: bounds(p - 1, p - 1 + 1) // CHECK: Block: B4, Pred: B5, Succ: B3 // CHECK: Widened bounds before stmt: a = 7 -// CHECK: p: bounds(p, p + 0) +// CHECK: p: bounds(p - 1, p - 1 + 1) // CHECK: Block: B3, Pred: B4, Succ: B5