Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -11619,6 +11619,12 @@ def err_bounds_type_annotation_lost_checking : Error<
def error_static_cast_bounds_invalid : Error<
"cast source bounds are too narrow for %0">;

def error_modified_return_bounds : Error<
"modified expression '%0' used in the declared return bounds for %1">;

def note_declared_return_bounds : Note<
"(expanded) declared return bounds are '%0'">;

def error_out_of_bounds_access : Error<
"out-of-bounds %select{||memory access|base value}0">;

Expand Down
82 changes: 76 additions & 6 deletions clang/lib/Sema/SemaBounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,20 @@ namespace {
uint64_t PointerWidth;
Stmt *Body;
CFG *Cfg;
BoundsExpr *ReturnBounds; // return bounds expression for enclosing
// function, if any.

// Declaration for enclosing function. Having this here allows us to emit
// the name of the function in any diagnostic message when checking return
// bounds.
FunctionDecl *FunctionDeclaration;
// Return value expression for enclosing function, if any. Having this
// here allows us to avoid reconstructing a return value for each
// return statement.
BoundsValueExpr *ReturnVal;
// Expanded declared return bounds expression for enclosing function, if
// any. Having this here allows us to avoid re-expanding the return bounds
// for each return statement.
BoundsExpr *ReturnBounds;

ASTContext &Context;
std::pair<ComparisonSet, ComparisonSet> &Facts;

Expand Down Expand Up @@ -2569,22 +2581,33 @@ namespace {


public:
CheckBoundsDeclarations(Sema &SemaRef, PrepassInfo &Info, Stmt *Body, CFG *Cfg, BoundsExpr *ReturnBounds, std::pair<ComparisonSet, ComparisonSet> &Facts) : S(SemaRef),
CheckBoundsDeclarations(Sema &SemaRef, PrepassInfo &Info, Stmt *Body, CFG *Cfg, FunctionDecl *FD, std::pair<ComparisonSet, ComparisonSet> &Facts) : S(SemaRef),
DumpBounds(SemaRef.getLangOpts().DumpInferredBounds),
DumpState(SemaRef.getLangOpts().DumpCheckingState),
DumpSynthesizedMembers(SemaRef.getLangOpts().DumpSynthesizedMembers),
PointerWidth(SemaRef.Context.getTargetInfo().getPointerWidth(0)),
Body(Body),
Cfg(Cfg),
ReturnBounds(ReturnBounds),
FunctionDeclaration(FD),
ReturnVal(nullptr),
ReturnBounds(nullptr),
Context(SemaRef.Context),
Facts(Facts),
BoundsWideningAnalyzer(BoundsWideningAnalysis(SemaRef, Cfg,
Info.BoundsVarsLower,
Info.BoundsVarsUpper)),
AbstractSetMgr(AbstractSetManager(SemaRef, Info.VarUses)),
BoundsSiblingFields(Info.BoundsSiblingFields),
IncludeNullTerminator(false) {}
IncludeNullTerminator(false) {
if (FD) {
ReturnVal =
new (S.Context) BoundsValueExpr(SourceLocation(),
FD->getReturnType(),
BoundsValueExpr::Kind::Return);
ReturnBounds =
BoundsUtil::ExpandToRange(S, ReturnVal, FD->getBoundsExpr());
}
}

CheckBoundsDeclarations(Sema &SemaRef, PrepassInfo &Info, std::pair<ComparisonSet, ComparisonSet> &Facts) : S(SemaRef),
DumpBounds(SemaRef.getLangOpts().DumpInferredBounds),
Expand All @@ -2593,6 +2616,8 @@ namespace {
PointerWidth(SemaRef.Context.getTargetInfo().getPointerWidth(0)),
Body(nullptr),
Cfg(nullptr),
FunctionDeclaration(nullptr),
ReturnVal(nullptr),
ReturnBounds(nullptr),
Context(SemaRef.Context),
Facts(Facts),
Expand Down Expand Up @@ -4526,6 +4551,10 @@ namespace {
return SrcBounds;
}

// Account for uses of LValue in the declared return bounds (if any)
// for the enclosing function.
UpdateReturnBoundsAfterAssignment(LValue, E, Src, CSS);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The name UpdateRetunrBoundsAfterAssignment may be misleading. There is really no "update" happening - my understanding is that this function checks if LValue occurs in ReturnBounds.


// Get the original value (if any) of LValue before the assignment,
// and determine whether the original value uses the value of LValue.
// OriginalValue is named OV in the Checked C spec.
Expand All @@ -4552,6 +4581,47 @@ namespace {
return ResultBounds;
}

// UpdateReturnBoundsAfterAssignment computes the observed bounds for the
// return value of the enclosing function (if any) by replacing all
// uses of LValue within the declared return bounds with null. If the
// declared return bounds use the value of LValue, the observed return
// bounds will be bounds(unknown) and an error will be emitted.
//
// The current implementation does not use invertibility of lvalue
// expressions. This simplifies the implementation so that the updated
// return bounds can be computed locally at each assignment statement,
// rather than keeping track of a return bounds expression across the
// entire function body.
// TODO: track an observed return bounds expression as a global property
// of the function body so that invertibility of lvalue expressions can
// be taken into account.
void UpdateReturnBoundsAfterAssignment(Expr *LValue, Expr *E, Expr *Src,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It looks like the third parameter Src is not used anywhere in this function.

CheckedScopeSpecifier CSS) {
if (!ReturnBounds || ReturnBounds->isUnknown() || ReturnBounds->isAny())
return;

// In unchecked scopes, if the enclosing function has its bounds declared
// via a bounds-safe interface, we do not check that expressions used in
// the declared function bounds are not modified.
if (CSS == CheckedScopeSpecifier::CSS_Unchecked) {
if (FunctionDeclaration->hasBoundsSafeInterface(Context))
return;
}

BoundsExpr *UpdatedReturnBounds =
BoundsUtil::ReplaceLValueInBounds(S, ReturnBounds, LValue,
nullptr, CSS);
if (!UpdatedReturnBounds->isUnknown())
return;

S.Diag(LValue->getBeginLoc(), diag::error_modified_return_bounds)
<< LValue << FunctionDeclaration << E->getSourceRange();

SourceLocation DeclaredLoc = FunctionDeclaration->getLocation();
S.Diag(DeclaredLoc, diag::note_declared_return_bounds)
<< ReturnBounds << ReturnBounds->getSourceRange();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suppose there were multiple program points in a function where a variable or a member expression used in ReturnBounds got modified, then would multiple error messages be emitted?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, there would be an error emitted at each assignment to a variable or member expression that is used in ReturnBounds.

}

// UpdateBoundsAfterAssignment updates the observed bounds context after
// an lvalue expression LValue is modified via an assignment E of the form
// LValue = Src, based on the state before the assignment.
Expand Down Expand Up @@ -6167,7 +6237,7 @@ void Sema::CheckFunctionBodyBoundsDecls(FunctionDecl *FD, Stmt *Body) {
BO.AddLifetime = true;
BO.AddNullStmt = true;
std::unique_ptr<CFG> Cfg = CFG::buildCFG(nullptr, Body, &getASTContext(), BO);
CheckBoundsDeclarations Checker(*this, Info, Body, Cfg.get(), FD->getBoundsExpr(), EmptyFacts);
CheckBoundsDeclarations Checker(*this, Info, Body, Cfg.get(), FD, EmptyFacts);
if (Cfg != nullptr) {
AvailableFactsAnalysis Collector(*this, Cfg.get());
Collector.Analyze();
Expand Down