diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 7ba490e62456e929459c6e2f88d5e47f5b8fc767..fa04d9c5f6c630a95b863da3fd18e35ac59f48b1 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1676,6 +1676,8 @@ def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">, } // end alpha.webkit +// OHOS_LOCAL begin + //===----------------------------------------------------------------------===// // OpenHarmony checkers. //===----------------------------------------------------------------------===// @@ -1688,4 +1690,28 @@ def UnixAPIArgsChecker : Checker<"UnixAPIArgs">, def MemcpyChecker : Checker<"Memcpy">, HelpText<"Check for memcpy_s api arguments">, Documentation; + +def PrintSensitiveInfoChecker : Checker<"PrintSensitiveInfo">, + HelpText<"Check for sensitive information disclosure">, + CheckerOptions<[ + CmdLineOption, + ]>, + Documentation; + +def SignalHandlerChecker : Checker<"SignalHandler">, + HelpText<"Check for signal handler">, + CheckerOptions<[ + CmdLineOption, + ]>, + Documentation; } + +// OHOS_LOCAL end diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 2a63b9446dcca66f9db782bbea4d87c29051040f..f7823b4090fbb1ff5ef174e0cd87d4285184f276 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -131,6 +131,8 @@ add_clang_library(clangStaticAnalyzerCheckers WebKit/UncountedLocalVarsChecker.cpp OpenHarmony/UnixAPIArgsChecker.cpp OpenHarmony/MemcpyChecker.cpp + OpenHarmony/PrintSensitiveInfoChecker.cpp + OpenHarmony/SignalHandlerChecker.cpp LINK_LIBS clangAST diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e613f05961f3ba2b4d1ec45677f73459c1cc4992 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp @@ -0,0 +1,304 @@ +//== PrintSensitiveInfoChecker.cpp ------------------------------*- C++ -*--==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines PrintSensitiveInfoChecker, checks for sensitive information leak +// such as printing password in log. +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "Yaml.h" + +using namespace std; +using namespace clang; +using namespace ento; + +namespace { + struct SensitiveState { + private: + bool sensitive; + + public: + bool isSensitive() const { return sensitive; } + void setSensitive(bool B) { sensitive = B; } + + bool operator==(const SensitiveState &X) const { + return sensitive == X.sensitive; + } + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddBoolean(sensitive); + } + }; + + class PrintSensitiveInfoChecker : public Checker, + check::PreStmt, + check::PreStmt> { + public: + // lowercase string in following sets + set m_sensitive_var_set; + set m_sensitive_func_set; + set m_output_set; + + struct SensitiveValueConfiguration { + struct SensitiveList { + string type; + vector list; + }; + vector sensitiveList; + }; + + PrintSensitiveInfoChecker(); + ~PrintSensitiveInfoChecker(){}; + + void checkPreStmt(const DeclStmt *declStmt, CheckerContext &c) const; + void checkPreStmt(const BinaryOperator *BO, CheckerContext &c) const; + void checkPreStmt(const CallExpr *call, CheckerContext &c) const; + + void saveVardeclStateForBo(const Expr *lhs, const Expr *rhs, CheckerContext &c) const; + void saveVardeclStateForDeclStmt(const DeclStmt *ds, CheckerContext &c) const; + const VarDecl *GetVarDeclFromExpr(const Expr *E) const; + string GetCurrentCalleeName(const CallExpr *CE) const; + + void parseConfiguration(CheckerManager &mgr, const std::string &Option, const SensitiveValueConfiguration *config); + string convertStrToLowerCase(string str) const; + + void report(const Stmt *Opera, const string &msg, CheckerContext &c) const; + }; +} + +using sensitiveConfig = PrintSensitiveInfoChecker::SensitiveValueConfiguration; +LLVM_YAML_IS_SEQUENCE_VECTOR(sensitiveConfig::SensitiveList) + +namespace llvm { + namespace yaml { + template <> + struct MappingTraits { + static void mapping(IO &io, sensitiveConfig &info) { + io.mapRequired("List", info.sensitiveList); + } + }; + + template <> + struct MappingTraits { + static void mapping(IO &io, sensitiveConfig::SensitiveList &info) { + io.mapRequired("type", info.type); + io.mapOptional("value", info.list); + } + }; + } // end namespace yaml +} // end namespace llvm + +REGISTER_MAP_WITH_PROGRAMSTATE(SensitiveInfoMap, const VarDecl *, SensitiveState) + +PrintSensitiveInfoChecker::PrintSensitiveInfoChecker() { + // some base patterns in set + m_sensitive_var_set.insert("password"); + m_sensitive_var_set.insert("passwd"); + m_sensitive_func_set.insert("getpassword"); + m_output_set.insert("hilog"); +} + +const VarDecl *PrintSensitiveInfoChecker::GetVarDeclFromExpr(const Expr *E) const { + if (const auto *DRE = dyn_cast(E)) { + if (const VarDecl *VD = dyn_cast(DRE->getDecl())) { + return VD; + } + } + return nullptr; +} + +string PrintSensitiveInfoChecker::GetCurrentCalleeName(const CallExpr *CE) const { + return CE->getDirectCallee()->getNameInfo().getName().getAsString(); +} + +string PrintSensitiveInfoChecker::convertStrToLowerCase(string str) const { + transform(str.begin(), str.end(), str.begin(), [](unsigned char c){ return tolower(c); }); + return str; +} + +void PrintSensitiveInfoChecker::parseConfiguration(CheckerManager &mgr, const std::string &Option, const SensitiveValueConfiguration *config) { + if (config) { + for (auto &sl : config->sensitiveList) { + if (sl.type == "fnCall") { + for (auto value : sl.list) { + m_sensitive_func_set.insert(convertStrToLowerCase(value)); + } + } + else if (sl.type == "varName") { + for (auto value : sl.list) { + m_sensitive_var_set.insert(convertStrToLowerCase(value)); + } + } + else if (sl.type == "outputFn") { + for (auto value : sl.list) { + m_output_set.insert(convertStrToLowerCase(value)); + } + } else { + mgr.reportInvalidCheckerOptionValue( + this, Option, + "a valid key: fnCall, varName, outputFn"); + } + } + } +} + +void PrintSensitiveInfoChecker::report(const Stmt *operaExpr, const string &msg, CheckerContext &context) const { + if (operaExpr == nullptr) { + return; + } + AnalysisDeclContext *declContext = + context.getAnalysisManager().getAnalysisDeclContext(context.getStackFrame()->getDecl()); + PathDiagnosticLocation location = + PathDiagnosticLocation::createBegin(operaExpr, context.getSourceManager(), declContext); + + SourceRange range = operaExpr->getSourceRange(); + if (!(declContext->getDecl())) { + return; + } + context.getBugReporter().EmitBasicReport(declContext->getDecl(), this, + "PrintSensitiveInfo", "Indicator of poor code quality", msg, + location, range); + context.addTransition(); +} + +void PrintSensitiveInfoChecker::checkPreStmt(const DeclStmt *ds, CheckerContext &c) const { + if (ds == nullptr) { + return; + } + saveVardeclStateForDeclStmt(ds, c); +} + +void PrintSensitiveInfoChecker::checkPreStmt(const BinaryOperator *binary, CheckerContext &c) const { + + if (binary == nullptr) { + return; + } + if (binary->getOpcode() == clang::BO_Assign) + { + const Expr *lhs = binary->getLHS()->IgnoreParenImpCasts(); + const Expr *rhs = binary->getRHS()->IgnoreParenImpCasts(); + if (lhs == nullptr || rhs == nullptr) + { + return; + } + saveVardeclStateForBo(lhs, rhs, c); + } +} + +void PrintSensitiveInfoChecker::checkPreStmt(const CallExpr *call, CheckerContext &c) const { + if (call == nullptr) { + return; + } + string funcName = GetCurrentCalleeName(call); + if (m_output_set.find(convertStrToLowerCase(funcName)) == m_output_set.end()) { + return; + } + unsigned int nums = call->getNumArgs(); + + for (unsigned int i = 0; i < nums; i++) { + const Expr *arg = call->getArg(i)->IgnoreParenImpCasts(); + if (arg == nullptr) { + continue; + } + + const VarDecl *varDecl = GetVarDeclFromExpr(arg); + if (varDecl == nullptr) { + continue; + } + // check by variable's name only + if (m_sensitive_var_set.find(convertStrToLowerCase(varDecl->getNameAsString())) != m_sensitive_var_set.end()) { + string msg = varDecl->getNameAsString() + " is a sensitive information"; + report(call, msg, c); + continue; + } + // check by state map + ProgramStateRef state = c.getState(); + const SensitiveState *sens = state->get(varDecl); + if (sens == nullptr) { + continue; + } + if (sens->isSensitive()) { + // report bug + string msg = varDecl->getNameAsString() + " is a sensitive information"; + report(call, msg, c); + } + } +} + +void PrintSensitiveInfoChecker::saveVardeclStateForBo(const Expr *lhs, const Expr *rhs, + CheckerContext &c) const { + if (rhs == nullptr || lhs == nullptr) { + return; + } + const VarDecl *varDecl = GetVarDeclFromExpr(lhs); + if (varDecl == nullptr) { + return; + } + if (isa(rhs)) { + const CallExpr *call = llvm::dyn_cast_or_null(rhs); + string funcName = GetCurrentCalleeName(call); + if (m_sensitive_func_set.find(convertStrToLowerCase(funcName)) != m_sensitive_func_set.end()) { + ProgramStateRef state = c.getState(); + SensitiveState sens; + sens.setSensitive(true); + state = state->set(varDecl, sens); + c.addTransition(state); + } + } +} + +void PrintSensitiveInfoChecker::saveVardeclStateForDeclStmt(const DeclStmt *ds, CheckerContext &c) const { + + const VarDecl *varDecl = llvm::dyn_cast_or_null(ds->getSingleDecl()); + if (varDecl == nullptr) { + return; + } + const Expr *expr = varDecl->getInit()->IgnoreParenImpCasts(); + if (expr == nullptr) { + return; + } + + if (isa(expr)) { + const CallExpr *call = llvm::dyn_cast_or_null(expr); + string funcName = GetCurrentCalleeName(call); + if (m_sensitive_func_set.find(convertStrToLowerCase(funcName)) != m_sensitive_func_set.end()) { + ProgramStateRef state = c.getState(); + SensitiveState sens; + sens.setSensitive(true); + state = state->set(varDecl, sens); + c.addTransition(state); + } + } +} + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerPrintSensitiveInfoChecker(CheckerManager &mgr) { + auto *Checker = mgr.registerChecker(); + string Option{"Config"}; + StringRef ConfigFile = mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option); + llvm::Optional obj = getConfiguration(mgr, Checker, Option, ConfigFile); + // If no Config is provided, obj is null + if (obj) { + Checker->parseConfiguration(mgr, Option, obj.getPointer()); + } +} + +// This checker should be enabled regardless of how language options are set. +bool ento::shouldRegisterPrintSensitiveInfoChecker(const CheckerManager &mgr) { + return true; +} diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..067a8e9cb1dee243dde38be373f884f50398a13d --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp @@ -0,0 +1,250 @@ +//== SignalHandlerChecker.cpp ------------------------------*- C++ -*--==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines SignalHandlerChecker, checks for calling non-async-safe +// functionos within signalhandler function +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/DenseSet.h" +#include "Yaml.h" + +using namespace std; +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + static bool isSystemCall(const FunctionDecl *FD) { + return FD->getASTContext().getSourceManager().isInSystemHeader( + FD->getCanonicalDecl()->getLocation()); + } + AST_MATCHER(FunctionDecl, isSystemCallCheck) { return isSystemCall(&Node); } + + class SignalHandlerChecker : public Checker { + public: + set m_async_safe_func_set; + struct FunctionConfiguration + { + struct FunctionList { + string type; + vector list; + }; + vector functionList; + }; + void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager &AM, + BugReporter &BR) const; + void parseConfiguration(CheckerManager &mgr, + const std::string &Option, + const FunctionConfiguration *config); + }; + + class Callback : public MatchFinder::MatchCallback { + const SignalHandlerChecker *C; + BugReporter &BR; + AnalysisDeclContext *ADC; + const llvm::StringSet<> StrictConformingFunctions = { + "signal", "abort", "_Exit", "quick_exit"}; + + public : + void run(const MatchFinder::MatchResult &Result) override; + void Report(const Stmt *stmt, const string &msg); + bool isSystemCallAllowed(const FunctionDecl *FD) const; + Callback(const SignalHandlerChecker *C, + BugReporter &BR, AnalysisDeclContext *ADC) + : C(C), BR(BR), ADC(ADC) {} + }; +} + +using functionConfig = SignalHandlerChecker::FunctionConfiguration; +LLVM_YAML_IS_SEQUENCE_VECTOR(functionConfig::FunctionList) + +namespace llvm +{ + namespace yaml + { + template <> + struct MappingTraits + { + static void mapping(IO &io, functionConfig &info) + { + io.mapRequired("List", info.functionList); + } + }; + + template <> + struct MappingTraits + { + static void mapping(IO &io, functionConfig::FunctionList &info) + { + io.mapRequired("type", info.type); + io.mapOptional("value", info.list); + } + }; + } // end namespace yaml +} // end namespace llvm + +void SignalHandlerChecker::parseConfiguration(CheckerManager &mgr, + const std::string &Option, + const FunctionConfiguration *config) { + if (config) { + for (auto &sl : config->functionList) { + if (sl.type == "asyncSafeFunction") { + for (auto &value : sl.list) { + m_async_safe_func_set.insert(value); + } + } else { + mgr.reportInvalidCheckerOptionValue( + this, Option, + "a valid key: asyncSafeFunction"); + } + } + } +} + +void SignalHandlerChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager &AM, + BugReporter &BR) const { + MatchFinder F; + Callback CB(this, BR, AM.getAnalysisDeclContext(TU)); + + auto SignalFunction = functionDecl(hasAnyName("signal", "::signal", "::std::signal"), + parameterCountIs(2), isSystemCallCheck()); + auto HandlerExpr = + declRefExpr(hasDeclaration(functionDecl().bind("handler_decl"))) + .bind("handler_expr"); + F.addMatcher((callExpr(callee(SignalFunction), hasArgument(1, HandlerExpr)) + .bind("register_call")), &CB); + F.matchAST(AM.getASTContext()); +} + +void Callback::run(const MatchFinder::MatchResult &Result) { + const auto *HandlerDecl = Result.Nodes.getNodeAs("handler_decl"); + const auto *HandlerExpr = Result.Nodes.getNodeAs("handler_expr"); + llvm::DenseSet SeenFunctions; + + // The worklist of the callgraph visitation algorithm. + queue CalledFunctions; + + auto ProcessFunction = [&](const FunctionDecl *F, const Expr *CallOrRef) { + if (F == nullptr || CallOrRef == nullptr) { + return false; + } + // Ensure that canonical declaration is used. + F = F->getCanonicalDecl(); + + // Do not visit function if already encountered. + if (!SeenFunctions.insert(F).second) + return true; + // Check if the call is allowed. + // Non-system calls are not considered. + if (isSystemCall(F)) { + if (isSystemCallAllowed(F)) { + return true; + } + // disallowed + const string funcName = F->getNameInfo().getName().getAsString(); + string msg = "The non-async-safe function '" + funcName + "' cannot be used in the callback function of signal"; + Report(CallOrRef, msg); + return false; + } + + // Get the body of the encountered non-system call function. + const FunctionDecl *FBody; + if (!F->hasBody(FBody)) { + const string funcName = F->getNameInfo().getName().getAsString(); + string msg = "The non-async-safe function '" + funcName + "' cannot be used in the callback function of signal"; + Report(CallOrRef, msg); + return false; + } + + // Collect all called functions. + auto Matches = match(decl(forEachDescendant(callExpr().bind("call"))), + *FBody, FBody->getASTContext()); + for (const auto &Match: Matches) { + const auto *CE = Match.getNodeAs("call"); + if (CE && isa(CE->getCalleeDecl())) { + CalledFunctions.push(CE); + } + } + return true; + }; + + if (!ProcessFunction(HandlerDecl, HandlerExpr)) + return; + + // Visit the definition of every function referenced by the handler function. + // Check for allowed function calls. + while (!CalledFunctions.empty()) { + const CallExpr *FunctionCall = CalledFunctions.front(); + CalledFunctions.pop(); + // At insertion we have already ensured that only function calls are there. + const auto *F = cast(FunctionCall->getCalleeDecl()); + + if (!ProcessFunction(F, FunctionCall)) + break; + } +} + +bool Callback::isSystemCallAllowed(const FunctionDecl *FD) const { + const IdentifierInfo *II = FD->getIdentifier(); + // Unnamed functions are not explicitly allowed. + if (!II) + return false; + + // user defined in yaml configuration + if (C->m_async_safe_func_set.count(II->getName().str())) { + return true; + } + + if (StrictConformingFunctions.count(II->getName())) + return true; + + return false; +} + +void Callback::Report(const Stmt *stmt, const std::string &msg) +{ + PathDiagnosticLocation ceLoc = PathDiagnosticLocation::createBegin(stmt, BR.getSourceManager(), ADC); + SourceRange r = stmt->getSourceRange(); + if (ADC->getDecl() == nullptr) { + return; + } + BR.EmitBasicReport(ADC->getDecl(), C, + "SignalHandlerChecker", "Indicator of Poor Code Quality", msg, ceLoc, r); +} + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerSignalHandlerChecker(CheckerManager &mgr) { + auto *Checker = mgr.registerChecker(); + std::string Option{"Config"}; + StringRef ConfigFile = mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option); + llvm::Optional obj = getConfiguration(mgr, Checker, Option, ConfigFile); + if (obj) { + Checker->parseConfiguration(mgr, Option, obj.getPointer()); + } +} + +// This checker should be enabled regardless of how language options are set. +bool ento::shouldRegisterSignalHandlerChecker(const CheckerManager &mgr) { + return true; +} diff --git a/clang/test/Analysis/print-sensitive-info.c b/clang/test/Analysis/print-sensitive-info.c new file mode 100644 index 0000000000000000000000000000000000000000..66d67373f7dd28e2d254fc29f90678bfc1de4082 --- /dev/null +++ b/clang/test/Analysis/print-sensitive-info.c @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=openharmony -verify %s + +void HILOG(int x, int y) { + return; +} + +int getPassword() { + return 1; +} + +void test1() { + int x = getPassword(); + int y = getPassword(); + HILOG(x, y); + // expected-warning@-1 {{x is a sensitive information}} + // expected-warning@-2 {{y is a sensitive information}} +} + +void test2(int password, int passwd) { + HILOG(password, passwd); + // expected-warning@-1 {{password is a sensitive information}} + // expected-warning@-2 {{passwd is a sensitive information}} +} diff --git a/clang/test/Analysis/signal-handler.c b/clang/test/Analysis/signal-handler.c new file mode 100644 index 0000000000000000000000000000000000000000..ad3025c5a0b27fab4a56eaae5b09c48bf3fb2197 --- /dev/null +++ b/clang/test/Analysis/signal-handler.c @@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=openharmony -verify %s +// expected-no-diagnostics + +#define SIGINT 1 +void sighandler1(int); +int printf(const char *, ...); +void signal(int, void *); + +int main () { + signal(SIGINT, sighandler1); + return 0; +} + +void sighandler1(int signum) { + printf("in signalhandler1"); +}