From 7afb40130eb1718f10b9e7dad779ddd401077023 Mon Sep 17 00:00:00 2001 From: Hongjin Li Date: Wed, 8 Feb 2023 16:40:09 +0800 Subject: [PATCH 1/2] feat: add SignalHandlerChecker and PrintSensitiveInfoChecker SignalHandlerChecker checks for calling non-async-function within a signal handler function. PrintSensitiveInfoChecker checks for sensitive information leak. Signed-off-by: Hongjin Li --- .../clang/StaticAnalyzer/Checkers/Checkers.td | 22 ++ .../StaticAnalyzer/Checkers/CMakeLists.txt | 2 + .../OpenHarmony/PrintSensitiveInfoChecker.cpp | 313 ++++++++++++++++++ .../OpenHarmony/SignalHandlerChecker.cpp | 243 ++++++++++++++ clang/test/Analysis/print-sensitive-info.c | 23 ++ clang/test/Analysis/signal-handler.c | 16 + 6 files changed, 619 insertions(+) create mode 100644 clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp create mode 100644 clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp create mode 100644 clang/test/Analysis/print-sensitive-info.c create mode 100644 clang/test/Analysis/signal-handler.c diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 7ba490e62456..3e365f559e1f 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1688,4 +1688,26 @@ 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; } diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 2a63b9446dcc..f7823b4090fb 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 000000000000..3c5e6fdab63d --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp @@ -0,0 +1,313 @@ +//== 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 { + public: + bool isSensitive; + SensitiveState() { + isSensitive = false; + } + + SensitiveState(const SensitiveState &another) { + this->isSensitive = another.isSensitive; + } + + SensitiveState &operator=(const SensitiveState &another) { + if (&another != this) { + this->isSensitive = another.isSensitive; + } + return *this; + } + + ~SensitiveState() {} + + bool operator==(const SensitiveState &x) const { + return isSensitive == x.isSensitive; + } + + void Profile(llvm::FoldingSetNodeID &id) const {} + }; + + class PrintSensitiveInfoChecker : public Checker, + check::PreStmt, + check::PreStmt> { + public: + // lowercase string in following sets + mutable std::set m_sensitive_var_set; + mutable std::set m_sensitive_func_set; + mutable std::set m_output_set; + + mutable map m_output_map; + + struct SensitiveValueConfiguration { + struct SensitiveList { + std::string type; + std::vector list; + }; + std::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; + const string GetCurrentCalleeName(const CallExpr *CE) const; + + void parseConfiguration(const SensitiveValueConfiguration *config); + string convertStrToLowerCase(const 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("pwd"); + 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; +} + +const string PrintSensitiveInfoChecker::GetCurrentCalleeName(const CallExpr *CE) const { + return CE->getDirectCallee()->getNameInfo().getName().getAsString(); +} + +string PrintSensitiveInfoChecker::convertStrToLowerCase(const string &str) const { + string ret(str); + transform(str.begin(), str.end(), ret.begin(), [](unsigned char c){ return tolower(c); }); + return ret; +} + +void PrintSensitiveInfoChecker::parseConfiguration(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)); + } + } + } + } +} + +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; + } + const 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); + } + // 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); + const string funcName = GetCurrentCalleeName(call); + if (m_sensitive_func_set.find(convertStrToLowerCase(funcName)) != m_sensitive_func_set.end()) { + ProgramStateRef state = c.getState(); + SensitiveState sens; + sens.isSensitive = 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); + const string funcName = GetCurrentCalleeName(call); + if (m_sensitive_func_set.find(convertStrToLowerCase(funcName)) != m_sensitive_func_set.end()) { + ProgramStateRef state = c.getState(); + SensitiveState sens; + sens.isSensitive = true; + state = state->set(varDecl, sens); + c.addTransition(state); + } + } +} + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerPrintSensitiveInfoChecker(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 no Config is provided, obj is null + if (obj) { + Checker->parseConfiguration(obj.getPointer()); + } +} + +// This checker should be enabled regardless of how language options are set. +bool ento::shouldRegisterPrintSensitiveInfoChecker(const CheckerManager &mgr) { + return true; +} \ No newline at end of file diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp new file mode 100644 index 000000000000..06382d93742b --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp @@ -0,0 +1,243 @@ +//== 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: + mutable std::set m_async_safe_func_set; + struct FunctionConfiguration + { + struct FunctionList { + std::string type; + std::vector list; + }; + std::vector functionList; + }; + void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager &AM, + BugReporter &BR) const; + void parseConfiguration(const FunctionConfiguration *config); + }; + + class Callback : public MatchFinder::MatchCallback { + const SignalHandlerChecker *C; + BugReporter &BR; + AnalysisDeclContext *ADC; + + 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) {} + + private: + llvm::StringSet<> StrictConformingFunctions = { + "signal", "abort", "_Exit", "quick_exit"}; + }; +} + +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(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); + } + } + } + } +} + +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 *SignalCall = Result.Nodes.getNodeAs("register_call"); + 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. + std::deque CalledFunctions; + + auto ProcessFunction = [&](const FunctionDecl *F, const Expr *CallOrRef) { + // 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 (isa(CE->getCalleeDecl())) { + CalledFunctions.push_back(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_front(); + // 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(obj.getPointer()); + } +} + +// This checker should be enabled regardless of how language options are set. +bool ento::shouldRegisterSignalHandlerChecker(const CheckerManager &mgr) { + return true; +} \ No newline at end of file diff --git a/clang/test/Analysis/print-sensitive-info.c b/clang/test/Analysis/print-sensitive-info.c new file mode 100644 index 000000000000..b381a4cc5034 --- /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 PWD) { + HILOG(password, PWD); + // expected-warning@-1 {{password is a sensitive information}} + // expected-warning@-2 {{PWD is a sensitive information}} +} \ No newline at end of file diff --git a/clang/test/Analysis/signal-handler.c b/clang/test/Analysis/signal-handler.c new file mode 100644 index 000000000000..8ceafccf5d2e --- /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"); +} \ No newline at end of file -- Gitee From b80530b61dc13d3007c45faa64cc3bf634ee13c3 Mon Sep 17 00:00:00 2001 From: Hongjin Li Date: Tue, 14 Feb 2023 15:35:05 +0800 Subject: [PATCH 2/2] review fix Signed-off-by: Hongjin Li --- .../clang/StaticAnalyzer/Checkers/Checkers.td | 4 + .../OpenHarmony/PrintSensitiveInfoChecker.cpp | 91 +++++++++---------- .../OpenHarmony/SignalHandlerChecker.cpp | 53 ++++++----- clang/test/Analysis/print-sensitive-info.c | 8 +- clang/test/Analysis/signal-handler.c | 2 +- 5 files changed, 80 insertions(+), 78 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 3e365f559e1f..fa04d9c5f6c6 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. //===----------------------------------------------------------------------===// @@ -1711,3 +1713,5 @@ def SignalHandlerChecker : Checker<"SignalHandler">, ]>, Documentation; } + +// OHOS_LOCAL end diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp index 3c5e6fdab63d..e613f05961f3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/PrintSensitiveInfoChecker.cpp @@ -27,30 +27,19 @@ using namespace ento; namespace { struct SensitiveState { + private: + bool sensitive; + public: - bool isSensitive; - SensitiveState() { - isSensitive = false; - } + bool isSensitive() const { return sensitive; } + void setSensitive(bool B) { sensitive = B; } - SensitiveState(const SensitiveState &another) { - this->isSensitive = another.isSensitive; + bool operator==(const SensitiveState &X) const { + return sensitive == X.sensitive; } - - SensitiveState &operator=(const SensitiveState &another) { - if (&another != this) { - this->isSensitive = another.isSensitive; - } - return *this; + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddBoolean(sensitive); } - - ~SensitiveState() {} - - bool operator==(const SensitiveState &x) const { - return isSensitive == x.isSensitive; - } - - void Profile(llvm::FoldingSetNodeID &id) const {} }; class PrintSensitiveInfoChecker : public Checker, @@ -58,18 +47,16 @@ namespace { check::PreStmt> { public: // lowercase string in following sets - mutable std::set m_sensitive_var_set; - mutable std::set m_sensitive_func_set; - mutable std::set m_output_set; - - mutable map m_output_map; + set m_sensitive_var_set; + set m_sensitive_func_set; + set m_output_set; struct SensitiveValueConfiguration { struct SensitiveList { - std::string type; - std::vector list; + string type; + vector list; }; - std::vector sensitiveList; + vector sensitiveList; }; PrintSensitiveInfoChecker(); @@ -82,10 +69,10 @@ namespace { 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; - const string GetCurrentCalleeName(const CallExpr *CE) const; + string GetCurrentCalleeName(const CallExpr *CE) const; - void parseConfiguration(const SensitiveValueConfiguration *config); - string convertStrToLowerCase(const string &str) 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; }; @@ -118,7 +105,7 @@ 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("pwd"); + m_sensitive_var_set.insert("passwd"); m_sensitive_func_set.insert("getpassword"); m_output_set.insert("hilog"); } @@ -132,33 +119,36 @@ const VarDecl *PrintSensitiveInfoChecker::GetVarDeclFromExpr(const Expr *E) cons return nullptr; } -const string PrintSensitiveInfoChecker::GetCurrentCalleeName(const CallExpr *CE) const { +string PrintSensitiveInfoChecker::GetCurrentCalleeName(const CallExpr *CE) const { return CE->getDirectCallee()->getNameInfo().getName().getAsString(); } -string PrintSensitiveInfoChecker::convertStrToLowerCase(const string &str) const { - string ret(str); - transform(str.begin(), str.end(), ret.begin(), [](unsigned char c){ return tolower(c); }); - return ret; +string PrintSensitiveInfoChecker::convertStrToLowerCase(string str) const { + transform(str.begin(), str.end(), str.begin(), [](unsigned char c){ return tolower(c); }); + return str; } -void PrintSensitiveInfoChecker::parseConfiguration(const SensitiveValueConfiguration *config) { +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) { + for (auto value : sl.list) { m_sensitive_func_set.insert(convertStrToLowerCase(value)); } } else if (sl.type == "varName") { - for (auto &value : sl.list) { + for (auto value : sl.list) { m_sensitive_var_set.insert(convertStrToLowerCase(value)); } } else if (sl.type == "outputFn") { - for (auto &value : sl.list) { + for (auto value : sl.list) { m_output_set.insert(convertStrToLowerCase(value)); } + } else { + mgr.reportInvalidCheckerOptionValue( + this, Option, + "a valid key: fnCall, varName, outputFn"); } } } @@ -211,7 +201,7 @@ void PrintSensitiveInfoChecker::checkPreStmt(const CallExpr *call, CheckerContex if (call == nullptr) { return; } - const string funcName = GetCurrentCalleeName(call); + string funcName = GetCurrentCalleeName(call); if (m_output_set.find(convertStrToLowerCase(funcName)) == m_output_set.end()) { return; } @@ -231,6 +221,7 @@ void PrintSensitiveInfoChecker::checkPreStmt(const CallExpr *call, CheckerContex 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(); @@ -238,7 +229,7 @@ void PrintSensitiveInfoChecker::checkPreStmt(const CallExpr *call, CheckerContex if (sens == nullptr) { continue; } - if (sens->isSensitive) { + if (sens->isSensitive()) { // report bug string msg = varDecl->getNameAsString() + " is a sensitive information"; report(call, msg, c); @@ -257,11 +248,11 @@ void PrintSensitiveInfoChecker::saveVardeclStateForBo(const Expr *lhs, const Exp } if (isa(rhs)) { const CallExpr *call = llvm::dyn_cast_or_null(rhs); - const string funcName = GetCurrentCalleeName(call); + string funcName = GetCurrentCalleeName(call); if (m_sensitive_func_set.find(convertStrToLowerCase(funcName)) != m_sensitive_func_set.end()) { ProgramStateRef state = c.getState(); SensitiveState sens; - sens.isSensitive = true; + sens.setSensitive(true); state = state->set(varDecl, sens); c.addTransition(state); } @@ -281,11 +272,11 @@ void PrintSensitiveInfoChecker::saveVardeclStateForDeclStmt(const DeclStmt *ds, if (isa(expr)) { const CallExpr *call = llvm::dyn_cast_or_null(expr); - const string funcName = GetCurrentCalleeName(call); + string funcName = GetCurrentCalleeName(call); if (m_sensitive_func_set.find(convertStrToLowerCase(funcName)) != m_sensitive_func_set.end()) { ProgramStateRef state = c.getState(); SensitiveState sens; - sens.isSensitive = true; + sens.setSensitive(true); state = state->set(varDecl, sens); c.addTransition(state); } @@ -298,16 +289,16 @@ void PrintSensitiveInfoChecker::saveVardeclStateForDeclStmt(const DeclStmt *ds, void ento::registerPrintSensitiveInfoChecker(CheckerManager &mgr) { auto *Checker = mgr.registerChecker(); - std::string Option{"Config"}; + 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(obj.getPointer()); + 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; -} \ No newline at end of file +} diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp index 06382d93742b..067a8e9cb1de 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/SignalHandlerChecker.cpp @@ -38,25 +38,29 @@ namespace { class SignalHandlerChecker : public Checker { public: - mutable std::set m_async_safe_func_set; + set m_async_safe_func_set; struct FunctionConfiguration { struct FunctionList { - std::string type; - std::vector list; + string type; + vector list; }; - std::vector functionList; + vector functionList; }; void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, AnalysisManager &AM, BugReporter &BR) const; - void parseConfiguration(const FunctionConfiguration *config); + 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; @@ -65,10 +69,6 @@ namespace { Callback(const SignalHandlerChecker *C, BugReporter &BR, AnalysisDeclContext *ADC) : C(C), BR(BR), ADC(ADC) {} - - private: - llvm::StringSet<> StrictConformingFunctions = { - "signal", "abort", "_Exit", "quick_exit"}; }; } @@ -100,13 +100,19 @@ namespace llvm } // end namespace yaml } // end namespace llvm -void SignalHandlerChecker::parseConfiguration(const FunctionConfiguration *config) { +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"); } } } @@ -129,16 +135,17 @@ void SignalHandlerChecker::checkEndOfTranslationUnit(const TranslationUnitDecl * } void Callback::run(const MatchFinder::MatchResult &Result) { - const auto *SignalCall = Result.Nodes.getNodeAs("register_call"); - const auto *HandlerDecl = - Result.Nodes.getNodeAs("handler_decl"); + 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. - std::deque CalledFunctions; + 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(); @@ -153,8 +160,8 @@ void Callback::run(const MatchFinder::MatchResult &Result) { } // 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); + string msg = "The non-async-safe function '" + funcName + "' cannot be used in the callback function of signal"; + Report(CallOrRef, msg); return false; } @@ -162,8 +169,8 @@ void Callback::run(const MatchFinder::MatchResult &Result) { 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); + string msg = "The non-async-safe function '" + funcName + "' cannot be used in the callback function of signal"; + Report(CallOrRef, msg); return false; } @@ -172,8 +179,8 @@ void Callback::run(const MatchFinder::MatchResult &Result) { *FBody, FBody->getASTContext()); for (const auto &Match: Matches) { const auto *CE = Match.getNodeAs("call"); - if (isa(CE->getCalleeDecl())) { - CalledFunctions.push_back(CE); + if (CE && isa(CE->getCalleeDecl())) { + CalledFunctions.push(CE); } } return true; @@ -186,7 +193,7 @@ void Callback::run(const MatchFinder::MatchResult &Result) { // Check for allowed function calls. while (!CalledFunctions.empty()) { const CallExpr *FunctionCall = CalledFunctions.front(); - CalledFunctions.pop_front(); + CalledFunctions.pop(); // At insertion we have already ensured that only function calls are there. const auto *F = cast(FunctionCall->getCalleeDecl()); @@ -233,11 +240,11 @@ void ento::registerSignalHandlerChecker(CheckerManager &mgr) { StringRef ConfigFile = mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option); llvm::Optional obj = getConfiguration(mgr, Checker, Option, ConfigFile); if (obj) { - Checker->parseConfiguration(obj.getPointer()); + 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; -} \ No newline at end of file +} diff --git a/clang/test/Analysis/print-sensitive-info.c b/clang/test/Analysis/print-sensitive-info.c index b381a4cc5034..66d67373f7dd 100644 --- a/clang/test/Analysis/print-sensitive-info.c +++ b/clang/test/Analysis/print-sensitive-info.c @@ -16,8 +16,8 @@ void test1() { // expected-warning@-2 {{y is a sensitive information}} } -void test2(int password, int PWD) { - HILOG(password, PWD); +void test2(int password, int passwd) { + HILOG(password, passwd); // expected-warning@-1 {{password is a sensitive information}} - // expected-warning@-2 {{PWD is a sensitive information}} -} \ No newline at end of file + // expected-warning@-2 {{passwd is a sensitive information}} +} diff --git a/clang/test/Analysis/signal-handler.c b/clang/test/Analysis/signal-handler.c index 8ceafccf5d2e..ad3025c5a0b2 100644 --- a/clang/test/Analysis/signal-handler.c +++ b/clang/test/Analysis/signal-handler.c @@ -13,4 +13,4 @@ int main () { void sighandler1(int signum) { printf("in signalhandler1"); -} \ No newline at end of file +} -- Gitee