Index: docs/analyzer/DebugChecks.rst =================================================================== --- docs/analyzer/DebugChecks.rst +++ docs/analyzer/DebugChecks.rst @@ -162,6 +162,41 @@ } while(0); // expected-warning{{SYMBOL DEAD}} +- void clang_analyzer_explain(a single argument of any type); + + This function explains the value of its argument in a human-readable manner + in the warning message. You can make as many overrides of its prototype + in the test code as necessary to explain various integral, pointer, + or even record-type values. + + Example usage:: + + void clang_analyzer_explain(int); + void clang_analyzer_explain(void *); + + void foo(int param, void *ptr) { + clang_analyzer_explain(param); // expected-warning{{argument 'param'}} + if (!ptr) + clang_analyzer_explain(ptr); // expected-warning{{memory address '0'}} + } + +- size_t clang_analyzer_getExtent(void *); + + This function returns the value that represents the extent of a memory region + pointed to by the argument. This value is often difficult to obtain otherwise, + because no valid code that produces this value. However, it may be useful + for testing purposes, to see how well does the analyzer model region extents. + + Example usage:: + + void foo() { + int x, *y; + size_t xs = clang_analyzer_getExtent(&x); + clang_analyzer_explain(xs); // expected-warning{{'4'}} + size_t ys = clang_analyzer_getExtent(&y); + clang_analyzer_explain(ys); // expected-warning{{'8'}} + } + Statistics ========== Index: include/clang/StaticAnalyzer/Checkers/SValExplainer.h =================================================================== --- /dev/null +++ include/clang/StaticAnalyzer/Checkers/SValExplainer.h @@ -0,0 +1,235 @@ +//== SValExplainer.h - Symbolic value explainer -----------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SValExplainer, a class for pretty-printing a +// human-readable description of a symbolic value. For example, +// "reg_$0" is turned into "initial value of variable 'x'". +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H +#define LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H + +#include "clang/AST/DeclCXX.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" + +namespace clang { + +namespace ento { + +class SValExplainer : public FullSValVisitor { +private: + ASTContext &ACtx; + + std::string printStmt(const Stmt *S) { + std::string Str; + llvm::raw_string_ostream OS(Str); + S->printPretty(OS, nullptr, PrintingPolicy(ACtx.getLangOpts())); + return OS.str(); + } + + bool isThisObject(const SymbolicRegion *R) { + if (auto S = dyn_cast(R->getSymbol())) + if (isa(S->getRegion())) + return true; + return false; + } + +public: + SValExplainer(ASTContext &Ctx) : ACtx(Ctx) {} + + std::string VisitUnknownVal(UnknownVal V) { + return "unknown value"; + } + + std::string VisitUndefinedVal(UndefinedVal V) { + return "undefined value"; + } + + std::string VisitLocMemRegionVal(loc::MemRegionVal V) { + const MemRegion *R = V.getRegion(); + // Avoid the weird "pointer to pointee of ...". + if (auto SR = dyn_cast(R)) { + // However, "pointer to 'this' object" is fine. + if (!isThisObject(SR)) + return Visit(SR->getSymbol()); + } + return "pointer to " + Visit(R); + } + + std::string VisitLocConcreteInt(loc::ConcreteInt V) { + llvm::APSInt I = V.getValue(); + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << "concrete memory address '" << I << "'"; + return OS.str(); + } + + std::string VisitNonLocSymbolVal(nonloc::SymbolVal V) { + return Visit(V.getSymbol()); + } + + std::string VisitNonLocConcreteInt(nonloc::ConcreteInt V) { + llvm::APSInt I = V.getValue(); + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << (I.isSigned() ? "signed " : "unsigned ") << I.getBitWidth() + << "-bit integer '" << I << "'"; + return OS.str(); + } + + std::string VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) { + return "lazily frozen compound value of " + Visit(V.getRegion()); + } + + std::string VisitSymbolRegionValue(const SymbolRegionValue *S) { + const MemRegion *R = S->getRegion(); + // Special handling for argument values. + if (auto V = dyn_cast(R)) + if (auto D = dyn_cast(V->getDecl())) + return "argument '" + D->getQualifiedNameAsString() + "'"; + return "initial value of " + Visit(R); + } + + std::string VisitSymbolConjured(const SymbolConjured *S) { + return "symbol of type '" + S->getType().getAsString() + + "' conjured at statement '" + printStmt(S->getStmt()) + "'"; + } + + std::string VisitSymbolDerived(const SymbolDerived *S) { + return "value derived from (" + Visit(S->getParentSymbol()) + + ") for " + Visit(S->getRegion()); + } + + std::string VisitSymbolExtent(const SymbolExtent *S) { + return "extent of " + Visit(S->getRegion()); + } + + std::string VisitSymbolMetadata(const SymbolMetadata *S) { + return "metadata of type '" + S->getType().getAsString() + "' tied to " + + Visit(S->getRegion()); + } + + std::string VisitSymIntExpr(const SymIntExpr *S) { + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << "(" << Visit(S->getLHS()) << ") " + << std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) << " " + << S->getRHS(); + return OS.str(); + } + + // TODO: IntSymExpr doesn't appear in practice. + // Add the relevant code once it does. + + std::string VisitSymSymExpr(const SymSymExpr *S) { + return "(" + Visit(S->getLHS()) + ") " + + std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) + + " (" + Visit(S->getRHS()) + ")"; + } + + std::string VisitSymbolCast(const SymbolCast *S) { + return "cast to '" + S->getType().getAsString() + "' of " + + Visit(S->getOperand()); + } + + std::string VisitSymbolicRegion(const SymbolicRegion *R) { + // Explain 'this' object here. + // TODO: Explain CXXThisRegion itself, find a way to test it. + if (isThisObject(R)) + return "'this' object"; + return "pointee of " + Visit(R->getSymbol()); + } + + std::string VisitAllocaRegion(const AllocaRegion *R) { + return "region allocated by '" + printStmt(R->getExpr()) + "'"; + } + + std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) { + return "compound literal " + printStmt(R->getLiteralExpr()); + } + + std::string VisitStringRegion(const StringRegion *R) { + return "string literal " + R->getString(); + } + + std::string VisitElementRegion(const ElementRegion *R) { + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << "element of type '" << R->getElementType().getAsString() + << "' with index "; + // For concrete index: omit type of the index integer. + if (auto I = R->getIndex().getAs()) + OS << I->getValue(); + else + OS << "'" << Visit(R->getIndex()) << "'"; + OS << " of " + Visit(R->getSuperRegion()); + return OS.str(); + } + + std::string VisitVarRegion(const VarRegion *R) { + const VarDecl *VD = R->getDecl(); + std::string Name = VD->getQualifiedNameAsString(); + if (isa(VD)) + return "parameter '" + Name + "'"; + else if (VD->hasLocalStorage()) + return "local variable '" + Name + "'"; + else if (VD->isStaticLocal()) + return "static local variable '" + Name + "'"; + else if (VD->hasGlobalStorage()) + return "global variable '" + Name + "'"; + else + llvm_unreachable("A variable is either local or global"); + } + + std::string VisitFieldRegion(const FieldRegion *R) { + return "field '" + R->getDecl()->getNameAsString() + "' of " + + Visit(R->getSuperRegion()); + } + + std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) { + return "temporary object constructed at statement '" + + printStmt(R->getExpr()) + "'"; + } + + std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) { + return "base object '" + R->getDecl()->getQualifiedNameAsString() + + "' inside " + Visit(R->getSuperRegion()); + } + + std::string VisitSVal(SVal V) { + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << V; + return "a value unsupported by the explainer: (" + + std::string(OS.str()) + ")"; + } + + std::string VisitSymExpr(SymbolRef S) { + std::string Str; + llvm::raw_string_ostream OS(Str); + S->dumpToStream(OS); + return "a symbolic expression unsupported by the explainer: (" + + std::string(OS.str()) + ")"; + } + + std::string VisitMemRegion(const MemRegion *R) { + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << R; + return "a memory region unsupported by the explainer (" + + std::string(OS.str()) + ")"; + } +}; + +} // end namespace ento + +} // end namespace clang + +#endif Index: include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -79,48 +79,11 @@ friend class MemRegionManager; public: enum Kind { - // Memory spaces. - CodeSpaceRegionKind, - StackLocalsSpaceRegionKind, - StackArgumentsSpaceRegionKind, - HeapSpaceRegionKind, - UnknownSpaceRegionKind, - StaticGlobalSpaceRegionKind, - GlobalInternalSpaceRegionKind, - GlobalSystemSpaceRegionKind, - GlobalImmutableSpaceRegionKind, - BEGIN_NON_STATIC_GLOBAL_MEMSPACES = GlobalInternalSpaceRegionKind, - END_NON_STATIC_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind, - BEGIN_GLOBAL_MEMSPACES = StaticGlobalSpaceRegionKind, - END_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind, - BEGIN_MEMSPACES = CodeSpaceRegionKind, - END_MEMSPACES = GlobalImmutableSpaceRegionKind, - // Untyped regions. - SymbolicRegionKind, - AllocaRegionKind, - // Typed regions. - BEGIN_TYPED_REGIONS, - FunctionCodeRegionKind = BEGIN_TYPED_REGIONS, - BlockCodeRegionKind, - BlockDataRegionKind, - BEGIN_TYPED_VALUE_REGIONS, - CompoundLiteralRegionKind = BEGIN_TYPED_VALUE_REGIONS, - CXXThisRegionKind, - StringRegionKind, - ObjCStringRegionKind, - ElementRegionKind, - // Decl Regions. - BEGIN_DECL_REGIONS, - VarRegionKind = BEGIN_DECL_REGIONS, - FieldRegionKind, - ObjCIvarRegionKind, - END_DECL_REGIONS = ObjCIvarRegionKind, - CXXTempObjectRegionKind, - CXXBaseObjectRegionKind, - END_TYPED_VALUE_REGIONS = CXXBaseObjectRegionKind, - END_TYPED_REGIONS = CXXBaseObjectRegionKind +#define REGION(Id, Parent) Id ## Kind, +#define REGION_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last, +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" }; - + private: const Kind kind; @@ -386,8 +349,7 @@ static bool classof(const MemRegion *R) { Kind k = R->getKind(); - return k >= StackLocalsSpaceRegionKind && - k <= StackArgumentsSpaceRegionKind; + return k >= BEGIN_STACK_MEMSPACES && k <= END_STACK_MEMSPACES; } }; @@ -549,7 +511,7 @@ static bool classof(const MemRegion* R) { Kind k = R->getKind(); - return k >= FunctionCodeRegionKind && k <= BlockCodeRegionKind; + return k >= BEGIN_CODE_TEXT_REGIONS && k <= END_CODE_TEXT_REGIONS; } }; Index: include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def =================================================================== --- /dev/null +++ include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def @@ -0,0 +1,89 @@ +//===-- Regions.def - Metadata about MemRegion kinds ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The list of regions (MemRegion sub-classes) used in the Static Analyzer. +// In order to use this information, users of this file must define one or more +// of the three macros: +// +// REGION(Id, Parent) - for specific MemRegion sub-classes, reserving +// enum value IdKind for their kind. +// +// ABSTRACT_REGION(Id, Parent) - for abstract region classes, +// +// REGION_RANGE(Id, First, Last) - for ranges of kind-enums, +// allowing to determine abstract class of a region +// based on the kind-enum value. +// +//===----------------------------------------------------------------------===// + +#ifndef REGION +#define REGION(Id, Parent) +#endif + +#ifndef ABSTRACT_REGION +#define ABSTRACT_REGION(Id, Parent) +#endif + +#ifndef REGION_RANGE +#define REGION_RANGE(Id, First, Last) +#endif + +ABSTRACT_REGION(MemSpaceRegion, MemRegion) + REGION(CodeSpaceRegion, MemSpaceRegion) + ABSTRACT_REGION(GlobalsSpaceRegion, MemSpaceRegion) + ABSTRACT_REGION(NonStaticGlobalSpaceRegion, GlobalsSpaceRegion) + REGION(GlobalImmutableSpaceRegion, NonStaticGlobalSpaceRegion) + REGION(GlobalInternalSpaceRegion, NonStaticGlobalSpaceRegion) + REGION(GlobalSystemSpaceRegion, NonStaticGlobalSpaceRegion) + REGION_RANGE(NON_STATIC_GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind, + GlobalSystemSpaceRegionKind) + REGION(StaticGlobalSpaceRegion, MemSpaceRegion) + REGION_RANGE(GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind, + StaticGlobalSpaceRegionKind) + REGION(HeapSpaceRegion, MemSpaceRegion) + ABSTRACT_REGION(StackSpaceRegion, MemSpaceRegion) + REGION(StackArgumentsSpaceRegion, StackSpaceRegion) + REGION(StackLocalsSpaceRegion, StackSpaceRegion) + REGION_RANGE(STACK_MEMSPACES, StackArgumentsSpaceRegionKind, + StackLocalsSpaceRegionKind) + REGION(UnknownSpaceRegion, MemSpaceRegion) + REGION_RANGE(MEMSPACES, CodeSpaceRegionKind, + UnknownSpaceRegionKind) +ABSTRACT_REGION(SubRegion, MemRegion) + REGION(AllocaRegion, SubRegion) + REGION(SymbolicRegion, SubRegion) + ABSTRACT_REGION(TypedRegion, SubRegion) + REGION(BlockDataRegion, TypedRegion) + ABSTRACT_REGION(CodeTextRegion, TypedRegion) + REGION(BlockCodeRegion, CodeTextRegion) + REGION(FunctionCodeRegion, CodeTextRegion) + REGION_RANGE(CODE_TEXT_REGIONS, BlockCodeRegionKind, + FunctionCodeRegionKind) + ABSTRACT_REGION(TypedValueRegion, TypedRegion) + REGION(CompoundLiteralRegion, TypedValueRegion) + REGION(CXXBaseObjectRegion, TypedValueRegion) + REGION(CXXTempObjectRegion, TypedValueRegion) + REGION(CXXThisRegion, TypedValueRegion) + ABSTRACT_REGION(DeclRegion, TypedValueRegion) + REGION(FieldRegion, DeclRegion) + REGION(ObjCIvarRegion, DeclRegion) + REGION(VarRegion, DeclRegion) + REGION_RANGE(DECL_REGIONS, FieldRegionKind, + VarRegionKind) + REGION(ElementRegion, TypedValueRegion) + REGION(ObjCStringRegion, TypedValueRegion) + REGION(StringRegion, TypedValueRegion) + REGION_RANGE(TYPED_VALUE_REGIONS, CompoundLiteralRegionKind, + StringRegionKind) + REGION_RANGE(TYPED_REGIONS, BlockDataRegionKind, + StringRegionKind) + +#undef REGION_RANGE +#undef ABSTRACT_REGION +#undef REGION Index: include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h =================================================================== --- /dev/null +++ include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h @@ -0,0 +1,151 @@ +//===--- SValVisitor.h - Visitor for SVal subclasses ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the SValVisitor, SymExprVisitor, and MemRegionVisitor +// interfaces, and also FullSValVisitor, which visits all three hierarchies. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" + +namespace clang { + +namespace ento { + +/// SValVisitor - this class implements a simple visitor for SVal +/// subclasses. +template class SValVisitor { +public: + +#define DISPATCH(NAME, CLASS) \ + return static_cast(this)->Visit ## NAME(V.castAs()) + + RetTy Visit(SVal V) { + // Dispatch to VisitFooVal for each FooVal. + // Take namespaces (loc:: and nonloc::) into account. + switch (V.getBaseKind()) { +#define BASIC_SVAL(Id, Parent) case SVal::Id ## Kind: DISPATCH(Id, Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + case SVal::LocKind: + switch (V.getSubKind()) { +#define LOC_SVAL(Id, Parent) \ + case loc::Id ## Kind: DISPATCH(Loc ## Id, loc :: Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + llvm_unreachable("Unknown Loc sub-kind!"); + case SVal::NonLocKind: + switch (V.getSubKind()) { +#define NONLOC_SVAL(Id, Parent) \ + case nonloc::Id ## Kind: DISPATCH(NonLoc ## Id, nonloc :: Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + llvm_unreachable("Unknown NonLoc sub-kind!"); + } + llvm_unreachable("Unknown SVal kind!"); + } + +#define BASIC_SVAL(Id, Parent) \ + RetTy Visit ## Id(Id V) { DISPATCH(Parent, Id); } +#define ABSTRACT_SVAL(Id, Parent) \ + BASIC_SVAL(Id, Parent) +#define LOC_SVAL(Id, Parent) \ + RetTy VisitLoc ## Id(loc::Id V) { DISPATCH(Parent, Parent); } +#define NONLOC_SVAL(Id, Parent) \ + RetTy VisitNonLoc ## Id(nonloc::Id V) { DISPATCH(Parent, Parent); } +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + + // Base case, ignore it. :) + RetTy VisitSVal(SVal V) { return RetTy(); } + +#undef DISPATCH +}; + +/// SymExprVisitor - this class implements a simple visitor for SymExpr +/// subclasses. +template class SymExprVisitor { +public: + +#define DISPATCH(CLASS) \ + return static_cast(this)->Visit ## CLASS(cast(S)) + + RetTy Visit(SymbolRef S) { + // Dispatch to VisitSymbolFoo for each SymbolFoo. + switch (S->getKind()) { +#define SYMBOL(Id, Parent) \ + case SymExpr::Id ## Kind: DISPATCH(Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + } + llvm_unreachable("Unknown SymExpr kind!"); + } + + // If the implementation chooses not to implement a certain visit method, fall + // back on visiting the superclass. +#define SYMBOL(Id, Parent) RetTy Visit ## Id(const Id *S) { DISPATCH(Parent); } +#define ABSTRACT_SYMBOL(Id, Parent) SYMBOL(Id, Parent) +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + + // Base case, ignore it. :) + RetTy VisitSymExpr(SymbolRef S) { return RetTy(); } + +#undef DISPATCH +}; + +/// MemRegionVisitor - this class implements a simple visitor for MemRegion +/// subclasses. +template class MemRegionVisitor { +public: + +#define DISPATCH(CLASS) \ + return static_cast(this)->Visit ## CLASS(cast(R)) + + RetTy Visit(const MemRegion *R) { + // Dispatch to VisitFooRegion for each FooRegion. + switch (R->getKind()) { +#define REGION(Id, Parent) case MemRegion::Id ## Kind: DISPATCH(Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + } + llvm_unreachable("Unknown MemRegion kind!"); + } + + // If the implementation chooses not to implement a certain visit method, fall + // back on visiting the superclass. +#define REGION(Id, Parent) \ + RetTy Visit ## Id(const Id *R) { DISPATCH(Parent); } +#define ABSTRACT_REGION(Id, Parent) \ + REGION(Id, Parent) +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + + // Base case, ignore it. :) + RetTy VisitMemRegion(const MemRegion *R) { return RetTy(); } + +#undef DISPATCH +}; + +/// FullSValVisitor - a convenient mixed visitor for all three: +/// SVal, SymExpr and MemRegion subclasses. +template +class FullSValVisitor : public SValVisitor, + public SymExprVisitor, + public MemRegionVisitor { +public: + using SValVisitor::Visit; + using SymExprVisitor::Visit; + using MemRegionVisitor::Visit; +}; + +} // end namespace ento + +} // end namespace clang + +#endif Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -45,11 +45,9 @@ public: enum BaseKind { // The enumerators must be representable using 2 bits. - UndefinedValKind = 0, // for subclass UndefinedVal (an uninitialized value) - UnknownValKind = 1, // for subclass UnknownVal (a void value) - LocKind = 2, // for subclass Loc (an L-value) - NonLocKind = 3 // for subclass NonLoc (an R-value that's not - // an L-value) +#define BASIC_SVAL(Id, Parent) Id ## Kind, +#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) Id ## Kind, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" }; enum { BaseBits = 2, BaseMask = 0x3 }; @@ -306,8 +304,10 @@ namespace nonloc { -enum Kind { ConcreteIntKind, SymbolValKind, - LocAsIntegerKind, CompoundValKind, LazyCompoundValKind }; +enum Kind { +#define NONLOC_SVAL(Id, Parent) Id ## Kind, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +}; /// \brief Represents symbolic expression. class SymbolVal : public NonLoc { @@ -465,7 +465,10 @@ namespace loc { -enum Kind { GotoLabelKind, MemRegionValKind, ConcreteIntKind }; +enum Kind { +#define LOC_SVAL(Id, Parent) Id ## Kind, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +}; class GotoLabel : public Loc { public: Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def =================================================================== --- /dev/null +++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def @@ -0,0 +1,75 @@ +//===-- SVals.def - Metadata about SVal kinds -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The list of symbolic values (SVal kinds and sub-kinds) used in the Static +// Analyzer. The distinction between loc:: and nonloc:: SVal namespaces is +// currently hardcoded, because it is too peculiar and explicit to be handled +// uniformly. In order to use this information, users of this file must define +// one or more of the following macros: +// +// BASIC_SVAL(Id, Parent) - for specific SVal sub-kinds, which are +// neither in loc:: nor in nonloc:: namespace; these classes occupy +// their own base kind IdKind. +// +// ABSTRACT_SVAL(Id, Parent) - for abstract SVal classes which are +// neither in loc:: nor in nonloc:: namespace, +// +// ABSTRACT_SVAL_WITH_KIND(Id, Parent) - for SVal classes which are also +// neither in loc:: nor in nonloc:: namespace, but occupy a whole base kind +// identifier IdKind, much like BASIC_SVALs. +// +// LOC_SVAL(Id, Parent) - for values in loc:: namespace, which occupy a sub-kind +// loc::IdKind. +// +// NONLOC_SVAL(Id, Parent) - for values in nonloc:: namespace, which occupy a +// sub-kind nonloc::IdKind. +// +//===----------------------------------------------------------------------===// + +#ifndef BASIC_SVAL +#define BASIC_SVAL(Id, Parent) +#endif + +#ifndef ABSTRACT_SVAL +#define ABSTRACT_SVAL(Id, Parent) +#endif + +#ifndef ABSTRACT_SVAL_WITH_KIND +#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) ABSTRACT_SVAL(Id, Parent) +#endif + +#ifndef LOC_SVAL +#define LOC_SVAL(Id, Parent) +#endif + +#ifndef NONLOC_SVAL +#define NONLOC_SVAL(Id, Parent) +#endif + +BASIC_SVAL(UndefinedVal, SVal) +ABSTRACT_SVAL(DefinedOrUnknownSVal, SVal) + BASIC_SVAL(UnknownVal, DefinedOrUnknownSVal) + ABSTRACT_SVAL(DefinedSVal, DefinedOrUnknownSVal) + ABSTRACT_SVAL_WITH_KIND(Loc, DefinedSVal) + LOC_SVAL(ConcreteInt, Loc) + LOC_SVAL(GotoLabel, Loc) + LOC_SVAL(MemRegionVal, Loc) + ABSTRACT_SVAL_WITH_KIND(NonLoc, DefinedSVal) + NONLOC_SVAL(CompoundVal, NonLoc) + NONLOC_SVAL(ConcreteInt, NonLoc) + NONLOC_SVAL(LazyCompoundVal, NonLoc) + NONLOC_SVAL(LocAsInteger, NonLoc) + NONLOC_SVAL(SymbolVal, NonLoc) + +#undef NONLOC_SVAL +#undef LOC_SVAL +#undef ABSTRACT_SVAL_WITH_KIND +#undef ABSTRACT_SVAL +#undef BASIC_SVAL + Index: include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -43,21 +43,10 @@ virtual void anchor(); public: enum Kind { - SymbolRegionValueKind, - SymbolConjuredKind, - SymbolDerivedKind, - SymbolExtentKind, - SymbolMetadataKind, - BEGIN_SYMBOLS = SymbolRegionValueKind, - END_SYMBOLS = SymbolMetadataKind, - SymIntExprKind, - IntSymExprKind, - SymSymExprKind, - BEGIN_BINARYSYMEXPRS = SymIntExprKind, - END_BINARYSYMEXPRS = SymSymExprKind, - SymbolCastKind +#define SYMBOL(Id, Parent) Id ## Kind, +#define SYMBOL_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last, +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" }; - private: Kind K; Index: include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def =================================================================== --- /dev/null +++ include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def @@ -0,0 +1,55 @@ +//===-- Symbols.def - Metadata about SymExpr kinds --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The list of symbols (SymExpr sub-classes) used in the Static Analyzer. +// In order to use this information, users of this file must define +// one or more of the three macros: +// +// SYMBOL(Id, Parent) - for specific SymExpr sub-classes, reserving the +// IdKind identifier for its kind enumeration value. +// +// ABSTRACT_SYMBOL(Id, Parent) - for abstract symbol classes, +// +// SYMBOL_RANGE(Id, First, Last) - for ranges of kind-enums, +// allowing to determine abstract class of a symbol +// based on the kind enumeration value. +// +//===----------------------------------------------------------------------===// + +#ifndef SYMBOL +#define SYMBOL(Id, Parent) +#endif + +#ifndef ABSTRACT_SYMBOL +#define ABSTRACT_SYMBOL(Id, Parent) +#endif + +#ifndef SYMBOL_RANGE +#define SYMBOL_RANGE(Id, First, Last) +#endif + +ABSTRACT_SYMBOL(BinarySymExpr, SymExpr) + SYMBOL(IntSymExpr, BinarySymExpr) + SYMBOL(SymIntExpr, BinarySymExpr) + SYMBOL(SymSymExpr, BinarySymExpr) +SYMBOL_RANGE(BINARYSYMEXPRS, IntSymExprKind, SymSymExprKind) + +SYMBOL(SymbolCast, SymExpr) + +ABSTRACT_SYMBOL(SymbolData, SymExpr) + SYMBOL(SymbolConjured, SymbolData) + SYMBOL(SymbolDerived, SymbolData) + SYMBOL(SymbolExtent, SymbolData) + SYMBOL(SymbolMetadata, SymbolData) + SYMBOL(SymbolRegionValue, SymbolData) +SYMBOL_RANGE(SYMBOLS, SymbolConjuredKind, SymbolRegionValueKind) + +#undef SYMBOL +#undef ABSTRACT_SYMBOL +#undef SYMBOL_RANGE Index: lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -11,6 +11,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Checkers/SValExplainer.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; @@ -25,17 +26,21 @@ void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; + void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; + void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const; typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, CheckerContext &C) const; + void reportBug(llvm::StringRef Msg, CheckerContext &C) const; + public: bool evalCall(const CallExpr *CE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; }; } -REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, const void *) +REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) bool ExprInspectionChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { @@ -50,6 +55,8 @@ &ExprInspectionChecker::analyzerWarnIfReached) .Case("clang_analyzer_warnOnDeadSymbol", &ExprInspectionChecker::analyzerWarnOnDeadSymbol) + .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) + .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) .Default(nullptr); if (!Handler) @@ -91,6 +98,18 @@ } } +void ExprInspectionChecker::reportBug(llvm::StringRef Msg, + CheckerContext &C) const { + if (!BT) + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + C.emitReport(llvm::make_unique(*BT, Msg, N)); +} + void ExprInspectionChecker::analyzerEval(const CallExpr *CE, CheckerContext &C) const { const LocationContext *LC = C.getPredecessor()->getLocationContext(); @@ -100,26 +119,12 @@ if (LC->getCurrentStackFrame()->getParent() != nullptr) return; - if (!BT) - BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); - - ExplodedNode *N = C.generateNonFatalErrorNode(); - if (!N) - return; - C.emitReport( - llvm::make_unique(*BT, getArgumentValueString(CE, C), N)); + reportBug(getArgumentValueString(CE, C), C); } void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const { - - if (!BT) - BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); - - ExplodedNode *N = C.generateNonFatalErrorNode(); - if (!N) - return; - C.emitReport(llvm::make_unique(*BT, "REACHABLE", N)); + reportBug("REACHABLE", C); } void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, @@ -134,14 +139,32 @@ if (LC->getCurrentStackFrame()->getParent() == nullptr) return; - if (!BT) - BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + reportBug(getArgumentValueString(CE, C), C); +} - ExplodedNode *N = C.generateNonFatalErrorNode(); - if (!N) - return; - C.emitReport( - llvm::make_unique(*BT, getArgumentValueString(CE, C), N)); +void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) + reportBug("Missing argument for explaining", C); + + SVal V = C.getSVal(CE->getArg(0)); + SValExplainer Ex(C.getASTContext()); + reportBug(Ex.Visit(V), C); +} + +void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) + reportBug("Missing region for obtaining extent", C); + + auto MR = dyn_cast_or_null(C.getSVal(CE->getArg(0)).getAsRegion()); + if (!MR) + reportBug("Obtaining extent of a non-region", C); + + ProgramStateRef State = C.getState(); + State = State->BindExpr(CE, C.getLocationContext(), + MR->getExtent(C.getSValBuilder())); + C.addTransition(State); } void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, @@ -163,20 +186,14 @@ ProgramStateRef State = C.getState(); const MarkedSymbolsTy &Syms = State->get(); for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { - SymbolRef Sym = static_cast(*I); + SymbolRef Sym = *I; if (!SymReaper.isDead(Sym)) continue; - if (!BT) - BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); - - ExplodedNode *N = C.generateNonFatalErrorNode(); - if (!N) - return; - - C.emitReport(llvm::make_unique(*BT, "SYMBOL DEAD", N)); - C.addTransition(State->remove(Sym), N); + reportBug("SYMBOL DEAD", C); + State = State->remove(Sym); } + C.addTransition(State); } void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, Index: test/Analysis/explain-svals.cpp =================================================================== --- /dev/null +++ test/Analysis/explain-svals.cpp @@ -0,0 +1,100 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core.builtin,debug.ExprInspection,unix.cstring -verify %s + +typedef unsigned long size_t; + +struct S { + struct S3 { + int y[10]; + }; + struct S2 : S3 { + int *x; + } s2[10]; + int z; +}; + + +void clang_analyzer_explain(int); +void clang_analyzer_explain(void *); +void clang_analyzer_explain(S); + +size_t clang_analyzer_getExtent(void *); + +size_t strlen(const char *); + +int conjure(); +S conjure_S(); + +int glob; +static int stat_glob; +void *glob_ptr; + +// Test strings are regex'ed because we need to match exact string +// rather than a substring. + +void test_1(int param, void *ptr) { + clang_analyzer_explain(&glob); // expected-warning-re{{{{^pointer to global variable 'glob'$}}}} + clang_analyzer_explain(param); // expected-warning-re{{{{^argument 'param'$}}}} + clang_analyzer_explain(ptr); // expected-warning-re{{{{^argument 'ptr'$}}}} + if (param == 42) + clang_analyzer_explain(param); // expected-warning-re{{{{^signed 32-bit integer '42'$}}}} +} + +void test_2(char *ptr, int ext) { + clang_analyzer_explain((void *) "asdf"); // expected-warning-re{{{{^pointer to element of type 'char' with index 0 of string literal "asdf"$}}}} + clang_analyzer_explain(strlen(ptr)); // expected-warning-re{{{{^metadata of type 'unsigned long' tied to pointee of argument 'ptr'$}}}} + clang_analyzer_explain(conjure()); // expected-warning-re{{{{^symbol of type 'int' conjured at statement 'conjure\(\)'$}}}} + clang_analyzer_explain(glob); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure\(\)'\) for global variable 'glob'$}}}} + clang_analyzer_explain(glob_ptr); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure\(\)'\) for global variable 'glob_ptr'$}}}} + clang_analyzer_explain(clang_analyzer_getExtent(ptr)); // expected-warning-re{{{{^extent of pointee of argument 'ptr'$}}}} + int *x = new int[ext]; + clang_analyzer_explain(x); // expected-warning-re{{{{^pointer to element of type 'int' with index 0 of pointee of symbol of type 'int \*' conjured at statement 'new int \[ext\]'$}}}} + // Sic! What gets computed is the extent of the element-region. + clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^signed 32-bit integer '4'$}}}} + delete[] x; + if (ext >= 256) + clang_analyzer_explain((char) ext); // expected-warning-re{{{{^cast to 'char' of argument 'ext'$}}}} +} + +void test_3(S s) { + clang_analyzer_explain(&s); // expected-warning-re{{{{^pointer to parameter 's'$}}}} + clang_analyzer_explain(s.z); // expected-warning-re{{{{^initial value of field 'z' of parameter 's'$}}}} + clang_analyzer_explain(&s.s2[5].y[3]); // expected-warning-re{{{{^pointer to element of type 'int' with index 3 of field 'y' of base object 'S::S3' inside element of type 'struct S::S2' with index 5 of field 's2' of parameter 's'$}}}} + if (!s.s2[7].x) { + clang_analyzer_explain(s.s2[7].x); // expected-warning-re{{{{^concrete memory address '0'$}}}} + // FIXME: we need to be explaining '1' rather than '0' here; not explainer bug. + clang_analyzer_explain(s.s2[7].x + 1); // expected-warning-re{{{{^concrete memory address '0'$}}}} + } +} + +void test_4(int x, int y) { + int z; + static int stat; + clang_analyzer_explain(x + 1); // expected-warning-re{{{{^\(argument 'x'\) \+ 1$}}}} + clang_analyzer_explain(1 + y); // expected-warning-re{{{{^\(argument 'y'\) \+ 1$}}}} + clang_analyzer_explain(x + y); // expected-warning-re{{{{^unknown value$}}}} + clang_analyzer_explain(z); // expected-warning-re{{{{^undefined value$}}}} + clang_analyzer_explain(&z); // expected-warning-re{{{{^pointer to local variable 'z'$}}}} + clang_analyzer_explain(stat); // expected-warning-re{{{{^signed 32-bit integer '0'$}}}} + clang_analyzer_explain(&stat); // expected-warning-re{{{{^pointer to static local variable 'stat'$}}}} + clang_analyzer_explain(stat_glob); // expected-warning-re{{{{^initial value of global variable 'stat_glob'$}}}} + clang_analyzer_explain(&stat_glob); // expected-warning-re{{{{^pointer to global variable 'stat_glob'$}}}} + clang_analyzer_explain((int[]){1, 2, 3}); // expected-warning-re{{{{^pointer to element of type 'int' with index 0 of compound literal \(int \[3\]\)\{1, 2, 3\}$}}}} +} + +namespace { +class C { + int x[10]; + +public: + void test_5(int i) { + clang_analyzer_explain(this); // expected-warning-re{{{{^pointer to 'this' object$}}}} + clang_analyzer_explain(&x[i]); // expected-warning-re{{{{^pointer to element of type 'int' with index 'argument 'i'' of field 'x' of 'this' object$}}}} + clang_analyzer_explain(__builtin_alloca(i)); // expected-warning-re{{{{^pointer to region allocated by '__builtin_alloca\(i\)'$}}}} + } +}; +} // end of anonymous namespace + +void test_6() { + clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$}}}} + clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'struct S' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}} +}