Skip to content

Commit 16506d7

Browse files
author
Gabor Marton
committed
[analyzer] StdLibraryFunctionsChecker: Add sanity checks for constraints
Summary: Once we found a matching FunctionDecl for the given summary then we validate the given constraints against that FunctionDecl. E.g. we validate that a NotNull constraint is applied only on arguments that have pointer types. This is needed because when we matched the signature of the summary we were working with incomplete function types, i.e. some intricate type could have been marked as `Irrelevant` in the signature. Reviewers: NoQ, Szelethus, balazske Subscribers: whisperity, xazax.hun, baloghadamsoftware, szepet, rnkovacs, a.sidorin, mikhail.ramalho, donat.nagy, dkrupp, gamesh411, Charusso, steakhal, ASDenysPetrov, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D77658
1 parent 16fef6d commit 16506d7

File tree

1 file changed

+146
-58
lines changed

1 file changed

+146
-58
lines changed

clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp

Lines changed: 146 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,8 @@ using namespace clang::ento;
6464
namespace {
6565
class StdLibraryFunctionsChecker
6666
: public Checker<check::PreCall, check::PostCall, eval::Call> {
67-
/// Below is a series of typedefs necessary to define function specs.
68-
/// We avoid nesting types here because each additional qualifier
69-
/// would need to be repeated in every function spec.
70-
struct Summary;
67+
68+
class Summary;
7169

7270
/// Specify how much the analyzer engine should entrust modeling this function
7371
/// to us. If he doesn't, he performs additional invalidations.
@@ -114,10 +112,27 @@ class StdLibraryFunctionsChecker
114112
virtual ValueConstraintPtr negate() const {
115113
llvm_unreachable("Not implemented");
116114
};
115+
116+
// Check whether the constraint is malformed or not. It is malformed if the
117+
// specified argument has a mismatch with the given FunctionDecl (e.g. the
118+
// arg number is out-of-range of the function's argument list).
119+
bool checkValidity(const FunctionDecl *FD) const {
120+
const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams();
121+
assert(ValidArg && "Arg out of range!");
122+
if (!ValidArg)
123+
return false;
124+
// Subclasses may further refine the validation.
125+
return checkSpecificValidity(FD);
126+
}
117127
ArgNo getArgNo() const { return ArgN; }
118128

119129
protected:
120130
ArgNo ArgN; // Argument to which we apply the constraint.
131+
132+
/// Do polymorphic sanity check on the constraint.
133+
virtual bool checkSpecificValidity(const FunctionDecl *FD) const {
134+
return true;
135+
}
121136
};
122137

123138
/// Given a range, should the argument stay inside or outside this range?
@@ -168,6 +183,14 @@ class StdLibraryFunctionsChecker
168183
}
169184
return std::make_shared<RangeConstraint>(Tmp);
170185
}
186+
187+
bool checkSpecificValidity(const FunctionDecl *FD) const override {
188+
const bool ValidArg =
189+
getArgType(FD, ArgN)->isIntegralType(FD->getASTContext());
190+
assert(ValidArg &&
191+
"This constraint should be applied on an integral type");
192+
return ValidArg;
193+
}
171194
};
172195

173196
class ComparisonConstraint : public ValueConstraint {
@@ -210,6 +233,13 @@ class StdLibraryFunctionsChecker
210233
Tmp.CannotBeNull = !this->CannotBeNull;
211234
return std::make_shared<NotNullConstraint>(Tmp);
212235
}
236+
237+
bool checkSpecificValidity(const FunctionDecl *FD) const override {
238+
const bool ValidArg = getArgType(FD, ArgN)->isPointerType();
239+
assert(ValidArg &&
240+
"This constraint should be applied only on a pointer type");
241+
return ValidArg;
242+
}
213243
};
214244

215245
// Represents a buffer argument with an additional size argument.
@@ -278,11 +308,52 @@ class StdLibraryFunctionsChecker
278308
typedef std::vector<ValueConstraintPtr> ConstraintSet;
279309

280310
using ArgTypes = std::vector<QualType>;
311+
312+
// A placeholder type, we use it whenever we do not care about the concrete
313+
// type in a Signature.
314+
const QualType Irrelevant{};
315+
bool static isIrrelevant(QualType T) { return T.isNull(); }
316+
317+
// The signature of a function we want to describe with a summary. This is a
318+
// concessive signature, meaning there may be irrelevant types in the
319+
// signature which we do not check against a function with concrete types.
320+
struct Signature {
321+
const ArgTypes ArgTys;
322+
const QualType RetTy;
323+
Signature(ArgTypes ArgTys, QualType RetTy) : ArgTys(ArgTys), RetTy(RetTy) {
324+
assertRetTypeSuitableForSignature(RetTy);
325+
for (size_t I = 0, E = ArgTys.size(); I != E; ++I) {
326+
QualType ArgTy = ArgTys[I];
327+
assertArgTypeSuitableForSignature(ArgTy);
328+
}
329+
}
330+
bool matches(const FunctionDecl *FD) const;
331+
332+
private:
333+
static void assertArgTypeSuitableForSignature(QualType T) {
334+
assert((T.isNull() || !T->isVoidType()) &&
335+
"We should have no void types in the spec");
336+
assert((T.isNull() || T.isCanonical()) &&
337+
"We should only have canonical types in the spec");
338+
}
339+
static void assertRetTypeSuitableForSignature(QualType T) {
340+
assert((T.isNull() || T.isCanonical()) &&
341+
"We should only have canonical types in the spec");
342+
}
343+
};
344+
345+
static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) {
346+
assert(FD && "Function must be set");
347+
QualType T = (ArgN == Ret)
348+
? FD->getReturnType().getCanonicalType()
349+
: FD->getParamDecl(ArgN)->getType().getCanonicalType();
350+
return T;
351+
}
352+
281353
using Cases = std::vector<ConstraintSet>;
282354

283-
/// Includes information about
284-
/// * function prototype (which is necessary to
285-
/// ensure we're modeling the right function and casting values properly),
355+
/// A summary includes information about
356+
/// * function prototype (signature)
286357
/// * approach to invalidation,
287358
/// * a list of branches - a list of list of ranges -
288359
/// A branch represents a path in the exploded graph of a function (which
@@ -299,15 +370,28 @@ class StdLibraryFunctionsChecker
299370
/// * a list of argument constraints, that must be true on every branch.
300371
/// If these constraints are not satisfied that means a fatal error
301372
/// usually resulting in undefined behaviour.
302-
struct Summary {
303-
const ArgTypes ArgTys;
304-
const QualType RetTy;
373+
///
374+
/// Application of a summary:
375+
/// The signature and argument constraints together contain information
376+
/// about which functions are handled by the summary. The signature can use
377+
/// "wildcards", i.e. Irrelevant types. Irrelevant type of a parameter in
378+
/// a signature means that type is not compared to the type of the parameter
379+
/// in the found FunctionDecl. Argument constraints may specify additional
380+
/// rules for the given parameter's type, those rules are checked once the
381+
/// signature is matched.
382+
class Summary {
383+
const Signature Sign;
305384
const InvalidationKind InvalidationKd;
306385
Cases CaseConstraints;
307386
ConstraintSet ArgConstraints;
308387

388+
// The function to which the summary applies. This is set after lookup and
389+
// match to the signature.
390+
const FunctionDecl *FD = nullptr;
391+
392+
public:
309393
Summary(ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd)
310-
: ArgTys(ArgTys), RetTy(RetTy), InvalidationKd(InvalidationKd) {}
394+
: Sign(ArgTys, RetTy), InvalidationKd(InvalidationKd) {}
311395

312396
Summary &Case(ConstraintSet&& CS) {
313397
CaseConstraints.push_back(std::move(CS));
@@ -318,24 +402,38 @@ class StdLibraryFunctionsChecker
318402
return *this;
319403
}
320404

321-
private:
322-
static void assertTypeSuitableForSummary(QualType T) {
323-
assert(!T->isVoidType() &&
324-
"We should have had no significant void types in the spec");
325-
assert(T.isCanonical() &&
326-
"We should only have canonical types in the spec");
327-
}
405+
InvalidationKind getInvalidationKd() const { return InvalidationKd; }
406+
const Cases &getCaseConstraints() const { return CaseConstraints; }
407+
const ConstraintSet &getArgConstraints() const { return ArgConstraints; }
328408

329-
public:
330409
QualType getArgType(ArgNo ArgN) const {
331-
QualType T = (ArgN == Ret) ? RetTy : ArgTys[ArgN];
332-
assertTypeSuitableForSummary(T);
333-
return T;
410+
return StdLibraryFunctionsChecker::getArgType(FD, ArgN);
334411
}
335412

336-
/// Try our best to figure out if the summary's signature matches
337-
/// *the* library function to which this specification applies.
338-
bool matchesSignature(const FunctionDecl *FD) const;
413+
// Returns true if the summary should be applied to the given function.
414+
// And if yes then store the function declaration.
415+
bool matchesAndSet(const FunctionDecl *FD) {
416+
bool Result = Sign.matches(FD) && validateByConstraints(FD);
417+
if (Result) {
418+
assert(!this->FD && "FD must not be set more than once");
419+
this->FD = FD;
420+
}
421+
return Result;
422+
}
423+
424+
private:
425+
// Once we know the exact type of the function then do sanity check on all
426+
// the given constraints.
427+
bool validateByConstraints(const FunctionDecl *FD) const {
428+
for (const ConstraintSet &Case : CaseConstraints)
429+
for (const ValueConstraintPtr &Constraint : Case)
430+
if (!Constraint->checkValidity(FD))
431+
return false;
432+
for (const ValueConstraintPtr &Constraint : ArgConstraints)
433+
if (!Constraint->checkValidity(FD))
434+
return false;
435+
return true;
436+
}
339437
};
340438

341439
// The map of all functions supported by the checker. It is initialized
@@ -345,11 +443,6 @@ class StdLibraryFunctionsChecker
345443

346444
mutable std::unique_ptr<BugType> BT_InvalidArg;
347445

348-
// Auxiliary functions to support ArgNo within all structures
349-
// in a unified manner.
350-
static QualType getArgType(const Summary &Summary, ArgNo ArgN) {
351-
return Summary.getArgType(ArgN);
352-
}
353446
static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) {
354447
return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN);
355448
}
@@ -406,7 +499,7 @@ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange(
406499
SValBuilder &SVB = Mgr.getSValBuilder();
407500
BasicValueFactory &BVF = SVB.getBasicValueFactory();
408501
ConstraintManager &CM = Mgr.getConstraintManager();
409-
QualType T = getArgType(Summary, getArgNo());
502+
QualType T = Summary.getArgType(getArgNo());
410503
SVal V = getArgSVal(Call, getArgNo());
411504

412505
if (auto N = V.getAs<NonLoc>()) {
@@ -433,7 +526,7 @@ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange(
433526
SValBuilder &SVB = Mgr.getSValBuilder();
434527
BasicValueFactory &BVF = SVB.getBasicValueFactory();
435528
ConstraintManager &CM = Mgr.getConstraintManager();
436-
QualType T = getArgType(Summary, getArgNo());
529+
QualType T = Summary.getArgType(getArgNo());
437530
SVal V = getArgSVal(Call, getArgNo());
438531

439532
// "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R".
@@ -489,13 +582,13 @@ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply(
489582
ProgramStateManager &Mgr = State->getStateManager();
490583
SValBuilder &SVB = Mgr.getSValBuilder();
491584
QualType CondT = SVB.getConditionType();
492-
QualType T = getArgType(Summary, getArgNo());
585+
QualType T = Summary.getArgType(getArgNo());
493586
SVal V = getArgSVal(Call, getArgNo());
494587

495588
BinaryOperator::Opcode Op = getOpcode();
496589
ArgNo OtherArg = getOtherArgNo();
497590
SVal OtherV = getArgSVal(Call, OtherArg);
498-
QualType OtherT = getArgType(Summary, OtherArg);
591+
QualType OtherT = Summary.getArgType(OtherArg);
499592
// Note: we avoid integral promotion for comparison.
500593
OtherV = SVB.evalCast(OtherV, T, OtherT);
501594
if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT)
@@ -514,9 +607,10 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
514607
ProgramStateRef State = C.getState();
515608

516609
ProgramStateRef NewState = State;
517-
for (const ValueConstraintPtr& VC : Summary.ArgConstraints) {
518-
ProgramStateRef SuccessSt = VC->apply(NewState, Call, Summary, C);
519-
ProgramStateRef FailureSt = VC->negate()->apply(NewState, Call, Summary, C);
610+
for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) {
611+
ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C);
612+
ProgramStateRef FailureSt =
613+
Constraint->negate()->apply(NewState, Call, Summary, C);
520614
// The argument constraint is not satisfied.
521615
if (FailureSt && !SuccessSt) {
522616
if (ExplodedNode *N = C.generateErrorNode(NewState))
@@ -546,10 +640,10 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
546640
ProgramStateRef State = C.getState();
547641

548642
// Apply case/branch specifications.
549-
for (const auto &VRS : Summary.CaseConstraints) {
643+
for (const ConstraintSet &Case : Summary.getCaseConstraints()) {
550644
ProgramStateRef NewState = State;
551-
for (const auto &VR: VRS) {
552-
NewState = VR->apply(NewState, Call, Summary, C);
645+
for (const ValueConstraintPtr &Constraint : Case) {
646+
NewState = Constraint->apply(NewState, Call, Summary, C);
553647
if (!NewState)
554648
break;
555649
}
@@ -566,7 +660,7 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
566660
return false;
567661

568662
const Summary &Summary = *FoundSummary;
569-
switch (Summary.InvalidationKd) {
663+
switch (Summary.getInvalidationKd()) {
570664
case EvalCallAsPure: {
571665
ProgramStateRef State = C.getState();
572666
const LocationContext *LC = C.getLocationContext();
@@ -585,27 +679,23 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
585679
llvm_unreachable("Unknown invalidation kind!");
586680
}
587681

588-
bool StdLibraryFunctionsChecker::Summary::matchesSignature(
682+
bool StdLibraryFunctionsChecker::Signature::matches(
589683
const FunctionDecl *FD) const {
590684
// Check number of arguments:
591685
if (FD->param_size() != ArgTys.size())
592686
return false;
593687

594-
// Check return type if relevant:
595-
if (!RetTy.isNull() && RetTy != FD->getReturnType().getCanonicalType())
596-
return false;
688+
// Check return type.
689+
if (!isIrrelevant(RetTy))
690+
if (RetTy != FD->getReturnType().getCanonicalType())
691+
return false;
597692

598-
// Check argument types when relevant:
693+
// Check argument types.
599694
for (size_t I = 0, E = ArgTys.size(); I != E; ++I) {
600-
QualType FormalT = ArgTys[I];
601-
// Null type marks irrelevant arguments.
602-
if (FormalT.isNull())
695+
QualType ArgTy = ArgTys[I];
696+
if (isIrrelevant(ArgTy))
603697
continue;
604-
605-
assertTypeSuitableForSummary(FormalT);
606-
607-
QualType ActualT = FD->getParamDecl(I)->getType().getCanonicalType();
608-
if (ActualT != FormalT)
698+
if (ArgTy != FD->getParamDecl(I)->getType().getCanonicalType())
609699
return false;
610700
}
611701

@@ -651,8 +741,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
651741
// of function summary for common cases (eg. ssize_t could be int or long
652742
// or long long, so three summary variants would be enough).
653743
// Of course, function variants are also useful for C++ overloads.
654-
const QualType
655-
Irrelevant{}; // A placeholder, whenever we do not care about the type.
656744
const QualType IntTy = ACtx.IntTy;
657745
const QualType LongTy = ACtx.LongTy;
658746
const QualType LongLongTy = ACtx.LongLongTy;
@@ -702,14 +790,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
702790
// Add a summary to a FunctionDecl found by lookup. The lookup is performed
703791
// by the given Name, and in the global scope. The summary will be attached
704792
// to the found FunctionDecl only if the signatures match.
705-
void operator()(StringRef Name, const Summary &S) {
793+
void operator()(StringRef Name, Summary S) {
706794
IdentifierInfo &II = ACtx.Idents.get(Name);
707795
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
708796
if (LookupRes.size() == 0)
709797
return;
710798
for (Decl *D : LookupRes) {
711799
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
712-
if (S.matchesSignature(FD)) {
800+
if (S.matchesAndSet(FD)) {
713801
auto Res = Map.insert({FD->getCanonicalDecl(), S});
714802
assert(Res.second && "Function already has a summary set!");
715803
(void)Res;

0 commit comments

Comments
 (0)