-
Notifications
You must be signed in to change notification settings - Fork 79
Use widened bounds to update bounds in context #821
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
Changes from 5 commits
928008c
90d162a
9499a01
e669212
2712d1e
2b4d323
10f3c4f
239391e
a4f71d0
db85f93
fb029a3
0a33ff8
23fd93b
da129b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2142,6 +2142,56 @@ namespace { | |
} | ||
} | ||
|
||
void ResetKilledBounds(StmtDeclSetTy &KilledBounds, Stmt *S, | ||
BoundsContextTy &ObservedBounds) { | ||
auto I = KilledBounds.find(S); | ||
if (I == KilledBounds.end()) | ||
return; | ||
|
||
// KilledBounds stores a mapping of statements to all variables whose | ||
// bounds are killed by each statement. Here are remove the bounds of all | ||
// such variables from ObservedBounds whose bounds are killed by the | ||
// statement S. After removal, the bounds of these variables would default | ||
// to the user declared bounds in DeclaredBounds. | ||
for (const VarDecl *V : I->second) | ||
ObservedBounds.erase(V); | ||
} | ||
|
||
void UpdateCtxWithWidenedBounds(BoundsMapTy &WidenedBounds, | ||
BoundsContextTy &ObservedBounds) { | ||
// WidenedBounds contains the mapping from _Nt_array_ptr to the offset by | ||
// which its declared bounds should be widened. In this function we apply | ||
// the offset to the declared bounds of the _Nt_array_ptr and update its | ||
// bounds in ObservedBounds. | ||
|
||
for (const auto item : WidenedBounds) { | ||
const VarDecl *V = item.first; | ||
unsigned Offset = item.second; | ||
|
||
// We normalize the declared bounds to RangBoundsExpr here so that we | ||
// can easily apply the offset to the upper bound. | ||
BoundsExpr *Bounds = S.ExpandBoundsToRange(V, V->getBoundsExpr()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here too we may be allocating AST data structures multiple times. We want to avoid doing that, and do the expansion only once. |
||
if (RangeBoundsExpr *RBE = dyn_cast<RangeBoundsExpr>(Bounds)) { | ||
const llvm::APInt | ||
APIntOff(Context.getTargetInfo().getPointerWidth(0), Offset); | ||
IntegerLiteral *WidenedOffset = CreateIntegerLiteral(APIntOff); | ||
|
||
Expr *Lower = RBE->getLowerExpr(); | ||
Expr *Upper = RBE->getUpperExpr(); | ||
|
||
// WidenedUpperBound = UpperBound + WidenedOffset. | ||
Expr *WidenedUpper = ExprCreatorUtil::CreateBinaryOperator( | ||
S, Upper, WidenedOffset, | ||
BinaryOperatorKind::BO_Add); | ||
|
||
RangeBoundsExpr *R = | ||
new (Context) RangeBoundsExpr(Lower, WidenedUpper, | ||
SourceLocation(), SourceLocation()); | ||
ObservedBounds[V] = R; | ||
} | ||
} | ||
} | ||
|
||
// Walk the CFG, traversing basic blocks in reverse post-oder. | ||
// For each element of a block, check bounds declarations. Skip | ||
// CFG elements that are subexpressions of other CFG elements. | ||
|
@@ -2177,13 +2227,28 @@ namespace { | |
StmtSet MemoryCheckedStmts; | ||
StmtSet BoundsCheckedStmts; | ||
IdentifyChecked(Body, MemoryCheckedStmts, BoundsCheckedStmts, CheckedScopeSpecifier::CSS_Unchecked); | ||
|
||
// Run the bounds widening analysis on this function. | ||
BoundsAnalysis BA = getBoundsAnalyzer(); | ||
BA.WidenBounds(FD); | ||
if (S.getLangOpts().DumpWidenedBounds) | ||
BA.DumpWidenedBounds(FD); | ||
|
||
PostOrderCFGView POView = PostOrderCFGView(Cfg); | ||
ResetFacts(); | ||
for (const CFGBlock *Block : POView) { | ||
AFA.GetFacts(Facts); | ||
CheckingState BlockState = GetIncomingBlockState(Block, BlockStates); | ||
// TODO: update BlockState.ObservedBounds to reflect the widened bounds | ||
// for the block. | ||
|
||
// Get the widened bounds for the current block as computed by the | ||
// bounds widening analysis invoked by WidenBounds above. | ||
BoundsMapTy WidenedBounds = BA.GetWidenedBounds(Block); | ||
// Also get the bounds killed (if any) by each statement in the current | ||
// block. | ||
StmtDeclSetTy KilledBounds = BA.GetKilledBounds(Block); | ||
// Update the Observed bounds with the widened bounds calculated above. | ||
UpdateCtxWithWidenedBounds(WidenedBounds, BlockState.ObservedBounds); | ||
|
||
for (CFGElement Elem : *Block) { | ||
if (Elem.getKind() == CFGElement::Statement) { | ||
CFGStmt CS = Elem.castAs<CFGStmt>(); | ||
|
@@ -2223,6 +2288,10 @@ namespace { | |
BoundsContextTy InitialObservedBounds = BlockState.ObservedBounds; | ||
BlockState.G.clear(); | ||
|
||
// If any bounds are killed by statement S, remove their bounds | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment no longer seems quite accurate, since the bounds that are killed by S are being reset to their declared bounds rather than removed from ObservedBounds. |
||
// from the ObservedBounds. | ||
ResetKilledBounds(KilledBounds, S, BlockState.ObservedBounds); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be done before saving For example:
|
||
|
||
Check(S, CSS, BlockState); | ||
|
||
if (DumpState) | ||
|
@@ -4632,8 +4701,22 @@ namespace { | |
// given target bounds. | ||
return TargetBounds; | ||
} | ||
case CastKind::CK_ArrayToPointerDecay: | ||
case CastKind::CK_ArrayToPointerDecay: { | ||
// For an array to pointer cast of a variable v, if v has observed | ||
// bounds, the rvalue bounds of the value of v should be the observed | ||
// bounds. This also accounts for variables with array type that have | ||
// widened bounds. | ||
if (DeclRefExpr *V = GetRValueVariable(E)) { | ||
if (const VarDecl *D = dyn_cast_or_null<VarDecl>(V->getDecl())) { | ||
if (BoundsExpr *B = State.ObservedBounds[D]) | ||
return B; | ||
} | ||
} | ||
// If an array to pointer cast e is not the value of a variable | ||
// with observed bounds, the rvalue bounds of e default to the | ||
// given lvalue bounds. | ||
return LValueBounds; | ||
} | ||
case CastKind::CK_DynamicPtrBounds: | ||
case CastKind::CK_AssumePtrBounds: | ||
llvm_unreachable("unexpected rvalue bounds cast"); | ||
|
@@ -5075,13 +5158,6 @@ void Sema::CheckFunctionBodyBoundsDecls(FunctionDecl *FD, Stmt *Body) { | |
Checker.Check(Body, CheckedScopeSpecifier::CSS_Unchecked); | ||
} | ||
|
||
if (Cfg != nullptr) { | ||
BoundsAnalysis BA = Checker.getBoundsAnalyzer(); | ||
BA.WidenBounds(FD); | ||
if (getLangOpts().DumpWidenedBounds) | ||
BA.DumpWidenedBounds(FD); | ||
} | ||
|
||
#if TRACE_CFG | ||
llvm::outs() << "Done " << FD->getName() << "\n"; | ||
#endif | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Tests for using bounds widening to control "out-of-bounds access" errors. | ||
// | ||
// RUN: %clang_cc1 -verify -verify-ignore-unexpected=note -fcheckedc-extension %s | ||
|
||
#include <limits.h> | ||
|
||
void f1() { | ||
_Nt_array_ptr<char> p : bounds(p, p) = ""; | ||
|
||
if (*p) | ||
if (*(p + 1)) | ||
if (*(p + 3)) // expected-error {{out-of-bounds memory access}} | ||
{} | ||
|
||
if (*p) { | ||
p++; | ||
if (*(p + 1)) // expected-error {{out-of-bounds memory access}} | ||
{} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should reset
ObservedBounds[V]
to the normalized declared bounds ofV
, rather than erasingV
from the context. Before callingCheck
on a statementS
, theObservedBounds
context should contain, for each variableV
that is in scope atS
:V
has widened bounds(lb, ub + i
) atS
andS
does not make an assignment toV
,ObservedBounds[V] => bounds(lb, ub + i)
V
has declared bounds(lb, ub)
,ObservedBounds[V] => bounds(lb, ub)
The
InitialObservedBounds
variable should also contain these bounds. After callingCheck
onS
,ObservedBounds
may contain updated bounds due to any assignments inS
. These updated observed bounds should be used to check that they imply the declared bounds for each variable. After this check,ObservedBounds
should be reset toInitialObservedBounds
and contain the bounds described above.For example: