Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -192,6 +192,10 @@ HelpText<"Check that addresses to stack memory do not escape the function">, DescFile<"StackAddrEscapeChecker.cpp">; +def PointerParamIgnoredChecker : Checker<"PointerParamIgnored">, + HelpText<"Check that the pointer written into inside the function is not ignored">, + DescFile<"PointerParamIgnoredChecker.cpp">; + } // end "alpha.core" let ParentPackage = Nullability in { Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -74,6 +74,7 @@ ObjCUnusedIVarsChecker.cpp PaddingChecker.cpp PointerArithChecker.cpp + PointerParamIgnoredChecker.cpp PointerSubChecker.cpp PthreadLockChecker.cpp RetainCountChecker.cpp Index: clang/lib/StaticAnalyzer/Checkers/PointerParamIgnoredChecker.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/PointerParamIgnoredChecker.cpp @@ -0,0 +1,215 @@ +//==- PointerParamIgnoredChecker.cpp ------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Matches: +// +// variable set to non-zero +// -> passed to a function with the name “error” or similar +// -> not written into +// -> not checked +// +// +// variable set to zero +// -> passed to a function with the name “error” or similar +// -> written into +// -> not checked +// +//===----------------------------------------------------------------------===// +// +// TODO: can do checkBranch condition +// TODO: OK how do I match condition + +#include "ClangSACheckers.h" +#include "llvm/ADT/FoldingSet.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +const char* ERR_PARAMETER_MATCHER = "err"; + +struct PassedPtrInfo { + /// Initial value written into. + llvm::APSInt ValueWrittenInto; + + /// Store the stack on where the first call with this location was + /// registered. + const StackFrameContext* StackOriginated; + + bool operator==(const PassedPtrInfo &Other) const { + return ValueWrittenInto == Other.ValueWrittenInto + && StackOriginated == Other.StackOriginated; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(ValueWrittenInto); + ID.Add(StackOriginated); + } + + PassedPtrInfo(llvm::APSInt V, const StackFrameContext *StackOriginated) + : ValueWrittenInto(V), StackOriginated(StackOriginated) {} +}; + +typedef llvm::ImmutableMap ErrorOutFlag; + +} // end anonymous namespace + +REGISTER_TRAIT_WITH_PROGRAMSTATE(ParamInfo, ErrorOutFlag) + +namespace { + +class PointerParamIgnoredChecker : public Checker { +private: + std::unique_ptr PointerIgnoredBugType; +public: + PointerParamIgnoredChecker() + : PointerIgnoredBugType(llvm::make_unique( + this, "Error pointer not checked", "Security")) {} + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const LocationContext* LC = C.getLocationContext(); + for (unsigned i=0; igetName(); + if (!ParamName.contains_lower(ERR_PARAMETER_MATCHER)) + continue; + + if (auto L = P.getAs()) { + SVal D = State->getSVal(*L); + + // Note: do not re-add already stored parameter information. + if (State->get(L->getAsRegion())) + continue; + + if (auto N = D.getAs()) { + llvm::APSInt Val = N->getValue(); + + State = State->set( + L->getAsRegion(), PassedPtrInfo(Val, LC->getCurrentStackFrame())); + } + } + } + + C.addTransition(State); + + } + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + for (unsigned i=0; igetName(); + if (!ParamName.contains_lower(ERR_PARAMETER_MATCHER)) + continue; + + if (auto L = P.getAs()) { + const PassedPtrInfo* Data = State->get(L->getAsRegion()); + if (!Data) + continue; + + // check that the value has changed. + SVal D = State->getSVal(*L); + auto N = D.getAs(); + // Value is no longer concrete or has definitely changed. + if (!N || N->getValue() != Data->ValueWrittenInto) + State = State->remove(L->getAsRegion()); + } + } + C.addTransition(State); + } + + void checkLocation(SVal P, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + const LocationContext* LC = C.getLocationContext(); + const StackFrameContext* SC = LC->getCurrentStackFrame(); + ProgramStateRef State = C.getState(); + if (!IsLoad) + return; + auto L = P.getAs(); + if (!L) + return; + + const PassedPtrInfo* Info = State->get(L->getAsRegion()); + if (!Info) + return; + + // Value was actually loaded into somewhere! + if (Info->StackOriginated == SC) + State = State->remove(L->getAsRegion()); + + C.addTransition(State); + } + + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + BugReporter &BR = C.getBugReporter(); + Optional ErrMsg; + for (auto P : State->get()) { + const MemRegion *MR = P.first; + PassedPtrInfo Info = P.second; + + if (!SymReaper.isLiveRegion(MR)) { + + ErrMsg = "Error pointer not checked"; + State = State->remove(MR); + return; + } + } + + if (ErrMsg) { + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + + // TODO: a better results messages, handle multiple errors, + // variable declaration, etc. + BR.emitReport(llvm::make_unique( + *PointerIgnoredBugType, "Error pointer not checked", N)); + } else { + + C.addTransition(State); + } + } + + // Remove invalidated regions. + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef ExplicitRegions, + ArrayRef Regions, + const LocationContext *LCtx, const CallEvent *Call) const { + for (const MemRegion *InvalidatedRegion : Regions) { + State = State->remove(InvalidatedRegion); + } + return State; + } + +}; + +} // namespace + +void ento::registerPointerParamIgnoredChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: clang/test/Analysis/pointerparamignoredchecker-test.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/pointerparamignoredchecker-test.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core.PointerParamIgnored -verify %s + +int errpassed(int *err) { + *err = 0; + return 1; +} + +int foo() { + int z = 0; + errpassed(&z); + return 1; +} // expected-warning{{Error pointer not checked}} +