diff --git a/.gitee/PULL_REQUEST_TEMPLATE.en.md b/.gitee/PULL_REQUEST_TEMPLATE.en.md index aa60947a3ec94a92aa3320fa9a1ae3508d654637..f6d433f6d1889bfeec2696275bd9eae65884796a 100644 --- a/.gitee/PULL_REQUEST_TEMPLATE.en.md +++ b/.gitee/PULL_REQUEST_TEMPLATE.en.md @@ -1,2 +1,4 @@ ## PR Description A clear and concise description of changes and rationale. Provide a link to external references / discussion if appropriate + +Development Guidelines: https://gitee.com/openharmony/third_party_llvm-project/blob/master/OpenHarmonyDevelopmentGuidelines.md diff --git a/OpenHarmonyDevelopmentGuidelines.md b/OpenHarmonyDevelopmentGuidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..212f6596d863b88a62bf8b88491b27c9a7934c8a --- /dev/null +++ b/OpenHarmonyDevelopmentGuidelines.md @@ -0,0 +1,37 @@ +## Generic Guidelines + +--------------------- + +### LLVM-related rules + +All changes should adhere to LLVM Developer Policy / Coding Standarts: +- https://llvm.org/docs/CodingStandards.html +- https://llvm.org/docs/DeveloperPolicy.html + +--------------------- + +### Mark changes properly + +All OHOS-related changes to mainline LLVM / clang code MUST be clearly marked such as: + +``` +unsigned LoopSizeThreshold = 32; // OHOS_LOCAL +``` + +or in case of multiline change: + +``` +// OHOS_LOCAL begin + +Some local OHOS change + +// OHOS_LOCAL begin +``` + +The presence of such marks greatly simplifies porting such code snippets to new LLVM versions. All such changes MUST be accompanied with a test case that MUST fail should the change is reverted. + +--------------------- + +### ABI Breakage + +All ABI-breaking changes MUST be scheduled to a major toolchain releases. One should explicitly discuss and document such changes. Ideally ABI-breaking change should cause linking error, it should not cause silent and hard to track bugs. diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 3b1326ffc37e22bb499679f676bc6dc7f3b373c0..df70f03f0939eae1ca123abeafca101b2b62753d 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1737,6 +1737,8 @@ def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">, } // end alpha.webkit +// OHOS_LOCAL begin + //===----------------------------------------------------------------------===// // OpenHarmony checkers. //===----------------------------------------------------------------------===// @@ -1749,4 +1751,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/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 68fe90c7a69d942158481c19bf9f4586cd7b3403..20c93c03a4c0728bd1b4c54c439f9ee7e3b3c83e 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -830,7 +830,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, TC.getTriple().isAndroid() || TC.getTriple().isOSFuchsia() || TC.getTriple().isOSDarwin()); - ImplicitCfiRuntime = TC.getTriple().isAndroid(); + ImplicitCfiRuntime = TC.getTriple().isAndroid() || TC.getTriple().isOHOSFamily(); if (AllAddedKinds & SanitizerKind::Address) { NeedPIE |= TC.getTriple().isOSFuchsia(); diff --git a/clang/lib/Driver/ToolChains/OHOS.cpp b/clang/lib/Driver/ToolChains/OHOS.cpp index 64dcec7bb1f299c8b3c9a26f7e1da93866f06cec..5293b92a0e5846687eb40609104316270622ac06 100644 --- a/clang/lib/Driver/ToolChains/OHOS.cpp +++ b/clang/lib/Driver/ToolChains/OHOS.cpp @@ -393,7 +393,8 @@ SanitizerMask OHOS::getSupportedSanitizers() const { Res |= SanitizerKind::Vptr; Res |= SanitizerKind::SafeStack; Res |= SanitizerKind::Scudo; - // TODO: kASAN for liteos ?? + Res |= SanitizerKind::KernelAddress; + Res |= SanitizerKind::KernelMemory; // TODO: Support TSAN and HWASAN and update mask. return Res; } diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 9a0c774b859a0f934265165b1c4609655a67f433..1749bd6233d6f3f5ce30ebf67c4776447206ba41 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -137,6 +137,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"); +} diff --git a/clang/test/Driver/sanitizer-ld.c b/clang/test/Driver/sanitizer-ld.c index 423ce8beaa84e4cd5eb9f2c340694b646f9423a5..09b5bdc9860e727086eb259eac11f628be30acf6 100644 --- a/clang/test/Driver/sanitizer-ld.c +++ b/clang/test/Driver/sanitizer-ld.c @@ -594,6 +594,14 @@ // CHECK-CFI-LINUX: "{{.*}}ld{{(.exe)?}}" // CHECK-CFI-LINUX-NOT: libclang_rt. +// [OHOS]CFI by itself does not link runtime libraries. +// RUN: %clang -fsanitize=cfi %s -### -o %t.o 2>&1 \ +// RUN: -target arm-linux-ohos -fuse-ld=lld -rtlib=platform \ +// RUN: --sysroot=%S/Inputs/ohos_native_tree/sysroot \ +// RUN: | FileCheck --check-prefix=CHECK-CFI-LINUX %s +// CHECK-CFI-LINUX: "{{.*}}ld{{(.exe)?}}" +// CHECK-CFI-LINUX-NOT: libclang_rt. + // CFI with diagnostics links the UBSan runtime. // RUN: %clang -fsanitize=cfi -fno-sanitize-trap=cfi -fsanitize-recover=cfi \ // RUN: -### %s 2>&1\ diff --git a/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h b/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h index 1f3aff45c49abe52a33c9b2745687b309119c517..c23c844b83c5d5febab4a6c6164e94f4fb4de327 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h +++ b/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h @@ -46,6 +46,8 @@ private: lldb::user_id_t m_breakpoint_site_id; bool m_auto_continue; bool m_reenabled_breakpoint_site; + bool m_stopped_at_my_breakpoint; + bool m_handling_signal; ThreadPlanStepOverBreakpoint(const ThreadPlanStepOverBreakpoint &) = delete; const ThreadPlanStepOverBreakpoint & diff --git a/lldb/source/Plugins/Platform/HOS/PlatformHOS.cpp b/lldb/source/Plugins/Platform/HOS/PlatformHOS.cpp index 808d2af4a0b7d0356bfc077e3a6277096fef2147..d31cadb0d1e6c85a1c39fefe4be084b937acf7e3 100644 --- a/lldb/source/Plugins/Platform/HOS/PlatformHOS.cpp +++ b/lldb/source/Plugins/Platform/HOS/PlatformHOS.cpp @@ -440,11 +440,8 @@ PlatformHOS::GetLibdlFunctionDeclarations(lldb_private::Process *process) { return PlatformPOSIX::GetLibdlFunctionDeclarations(process); } -HdcClient::SyncService *PlatformHOS::GetSyncService(Status &error) { - if (m_adb_sync_svc && m_adb_sync_svc->IsConnected()) - return m_adb_sync_svc.get(); - +std::unique_ptr PlatformHOS::GetSyncService(Status &error) { HdcClient hdc(m_device_id); - m_adb_sync_svc = hdc.GetSyncService(error); - return (error.Success()) ? m_adb_sync_svc.get() : nullptr; + std::unique_ptr adb_sync_svc = hdc.GetSyncService(error); + return (error.Success()) ? std::move(adb_sync_svc) : nullptr; } diff --git a/lldb/source/Plugins/Platform/HOS/PlatformHOS.h b/lldb/source/Plugins/Platform/HOS/PlatformHOS.h index d725972243ca8ef444b9771387cfc1c96e5c5b81..33d5c482042fc05a4b64c1d21e4f2a67162d6880 100644 --- a/lldb/source/Plugins/Platform/HOS/PlatformHOS.h +++ b/lldb/source/Plugins/Platform/HOS/PlatformHOS.h @@ -69,9 +69,8 @@ protected: GetLibdlFunctionDeclarations(lldb_private::Process *process) override; private: - HdcClient::SyncService *GetSyncService(Status &error); + std::unique_ptr GetSyncService(Status &error); - std::unique_ptr m_adb_sync_svc; std::string m_device_id; uint32_t m_sdk_version; }; diff --git a/lldb/source/Plugins/Platform/OHOS/HdcClient.cpp b/lldb/source/Plugins/Platform/OHOS/HdcClient.cpp index 6807d24cf317f4dc4a38769f57f1ea648b702110..1f08dc55f6422579240da066a5ae50361014d555 100644 --- a/lldb/source/Plugins/Platform/OHOS/HdcClient.cpp +++ b/lldb/source/Plugins/Platform/OHOS/HdcClient.cpp @@ -153,8 +153,14 @@ Status HdcClient::Connect() { std::string port = "8710"; const char *env_port = std::getenv("OHOS_HDC_SERVER_PORT"); - if ((env_port != NULL) && (atoi(env_port) > 0)) { - port = env_port; + if (env_port != NULL) { + int iEnv_port = atoi(env_port); + if ((iEnv_port > 0) && (iEnv_port <= 65535)) { + port = env_port; + } + else { + return Status("invalid port specification: %s. $OHOS_HDC_SERVER_PORT must be a positive number in (0,65535]", env_port); + } } std::string uri = "connect://localhost:" + port; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFProperties.td b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFProperties.td index 2f1ce88808b763f7ac2f6623fe0c92927d35de6d..ef6ae349858820c69cb15f16b709f623f104212b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFProperties.td +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFProperties.td @@ -1,6 +1,10 @@ include "../../../../include/lldb/Core/PropertiesBase.td" let Definition = "symbolfiledwarf" in { + def SymLinkPaths: Property<"comp-dir-symlink-paths", "FileSpecList">, + Global, + DefaultStringValue<"">, + Desc<"If the DW_AT_comp_dir matches any of these paths the symbolic links will be resolved at DWARF parse time.">; def IgnoreIndexes: Property<"ignore-file-indexes", "Boolean">, Global, DefaultFalse, diff --git a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp index f88a2b895931cde7abcd56beb9131c93b4878202..399a5ef62c6f56c7e4bcebcb30a77d4152d5b6c6 100644 --- a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp +++ b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -28,8 +28,9 @@ ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint(Thread &thread) // first in the thread plan stack when stepping over // a breakpoint m_breakpoint_addr(LLDB_INVALID_ADDRESS), - m_auto_continue(false), m_reenabled_breakpoint_site(false) - + m_auto_continue(false), m_reenabled_breakpoint_site(false), + m_stopped_at_my_breakpoint(false), + m_handling_signal(false) { m_breakpoint_addr = thread.GetRegisterContext()->GetPC(); m_breakpoint_site_id = @@ -48,6 +49,8 @@ void ThreadPlanStepOverBreakpoint::GetDescription( bool ThreadPlanStepOverBreakpoint::ValidatePlan(Stream *error) { return true; } bool ThreadPlanStepOverBreakpoint::DoPlanExplainsStop(Event *event_ptr) { + m_stopped_at_my_breakpoint = false; + StopInfoSP stop_info_sp = GetPrivateStopInfo(); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); @@ -80,16 +83,29 @@ bool ThreadPlanStepOverBreakpoint::DoPlanExplainsStop(Event *event_ptr) { lldb::addr_t pc_addr = GetThread().GetRegisterContext()->GetPC(); if (pc_addr == m_breakpoint_addr) { + m_stopped_at_my_breakpoint = true; + // If we came from a signal handler, just reset the flag and try again. + m_handling_signal = false; LLDB_LOGF(log, "Got breakpoint stop reason but pc: 0x%" PRIx64 - "hasn't changed.", + " hasn't changed, resetting m_handling_signal." + " If we came from a signal handler, trying again.", pc_addr); return true; } + // Even if we are in a signal handler, handle the breakpoint as usual + SetAutoContinue(false); return false; } + case eStopReasonSignal: + if (!m_handling_signal) { + // Next stop may be a signal handler. + LLDB_LOG(log, "Preparing for signal handler handling."); + m_handling_signal = true; + } + return false; default: return false; } @@ -104,6 +120,12 @@ bool ThreadPlanStepOverBreakpoint::ShouldStop(Event *event_ptr) { bool ThreadPlanStepOverBreakpoint::StopOthers() { return true; } StateType ThreadPlanStepOverBreakpoint::GetPlanRunState() { + if (m_handling_signal) { + // Resume & wait to hit our initial breakpoint + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOG(log, "Step over breakpoint resuming through a potential signal handler."); + return eStateRunning; + } return eStateStepping; } @@ -112,9 +134,19 @@ bool ThreadPlanStepOverBreakpoint::DoWillResume(StateType resume_state, if (current_plan) { BreakpointSiteSP bp_site_sp( m_process.GetBreakpointSiteList().FindByAddress(m_breakpoint_addr)); - if (bp_site_sp && bp_site_sp->IsEnabled()) { - m_process.DisableBreakpointSite(bp_site_sp.get()); - m_reenabled_breakpoint_site = false; + if (bp_site_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (m_handling_signal) { + // Turn the breakpoint back on and wait to hit it. + // Even if there is no userspace signal handler, we'll immediately stop + // on the breakpoint and try again. + LLDB_LOG(log, "Step over breakpoint reenabling breakpoint to try again after a potential signal handler"); + ReenableBreakpointSite(); + } else if (bp_site_sp->IsEnabled()) { + LLDB_LOG(log, "Step over breakpoint disabling breakpoint."); + m_process.DisableBreakpointSite(bp_site_sp.get()); + m_reenabled_breakpoint_site = false; + } } } return true; @@ -132,7 +164,7 @@ bool ThreadPlanStepOverBreakpoint::MischiefManaged() { if (pc_addr == m_breakpoint_addr) { // If we are still at the PC of our breakpoint, then for some reason we - // didn't get a chance to run. + // didn't get a chance to run, or we received a signal and want to try again. return false; } else { Log *log = GetLog(LLDBLog::Step); @@ -164,9 +196,16 @@ void ThreadPlanStepOverBreakpoint::SetAutoContinue(bool do_it) { } bool ThreadPlanStepOverBreakpoint::ShouldAutoContinue(Event *event_ptr) { + if (m_stopped_at_my_breakpoint) { + // Do not stop again at the breakpoint we are trying to step over + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOG(log, "Stopped step over breakpoint plan on its own breakpoint, auto-continue."); + return true; + } return m_auto_continue; } bool ThreadPlanStepOverBreakpoint::IsPlanStale() { - return GetThread().GetRegisterContext()->GetPC() != m_breakpoint_addr; + // TODO: validate + return !m_handling_signal && GetThread().GetRegisterContext()->GetPC() != m_breakpoint_addr; } diff --git a/llvm-build/README.md b/llvm-build/README.md index fd76e9fcf7559f59ce802ea6c7e313fb0e4655e7..caedd116f21c88530e0e7d5e49f5377334c5c0d4 100644 --- a/llvm-build/README.md +++ b/llvm-build/README.md @@ -52,6 +52,7 @@ build.py options: --enable-assertions # enable assertion when compiling --build-name # specify release package name --debug # build debug version llvm toolchain +--strip # strip llvm toolchain binaries --no-build-arm # skip triplet arm --no-build-aarch64 # skip triplet arm64 --no-build-x86_64 # skip triplet x86_64 diff --git a/llvm-build/build.py b/llvm-build/build.py index f2c08612a5b6cd4953cd49608ee3cd3e109722ea..65a85c3e06121e47428355f547ac0c9f128ccdc0 100755 --- a/llvm-build/build.py +++ b/llvm-build/build.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (C) 2021 Huawei Device Co., Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,6 +37,7 @@ class BuildConfig(): self.do_package = not args.skip_package self.build_name = args.build_name self.debug = args.debug + self.strip = args.strip self.no_lto = args.no_lto self.build_instrumented = args.build_instrumented self.xunit_xml_output = args.xunit_xml_output @@ -98,6 +99,12 @@ class BuildConfig(): default=False, help='Building Clang and LLVM Tools for Debugging (only affects stage2)') + parser.add_argument( + '--strip', + action='store_true', + default=False, + help='Strip final LLVM binaries.') + parser.add_argument( '--no-build-arm', action='store_true', @@ -429,6 +436,7 @@ class LlvmCore(BuildUtils): def llvm_compile_darwin_defines(self, llvm_defines): if self.host_is_darwin(): + llvm_defines['LIBUNWIND_ENABLE_SHARED'] = 'OFF' llvm_defines['LLDB_ENABLE_LIBEDIT'] = 'OFF' llvm_defines['LLDB_NO_DEBUGSERVER'] = 'ON' llvm_defines['LLDB_ENABLE_PYTHON'] = 'ON' @@ -535,7 +543,9 @@ class LlvmCore(BuildUtils): cflags = '-fstack-protector-strong -fPIE' if not self.host_is_darwin(): - ldflags += ' -Wl,-z,relro,-z,now -pie -s' + ldflags += ' -Wl,-z,relro,-z,now -pie' + if self.build_config.strip: + ldflags += ' -s' self.llvm_compile_llvm_defines(llvm_defines, llvm_cc, llvm_cxx, cflags, ldflags) @@ -819,7 +829,9 @@ class LlvmLibs(BuildUtils): '-stdlib=libc++', ] if not self.host_is_darwin(): - ldflag.append('-Wl,-z,relro,-z,now -s -pie') + ldflag.append('-Wl,-z,relro,-z,now -pie') + if self.build_config.strip: + ldflag.append('-s') ldflags.extend(ldflag) @@ -1247,7 +1259,9 @@ class LldbMi(BuildUtils): cflags = [] cxxflags =[] ldflags = ['-fuse-ld=lld', '-Wl,-rpath,%s' % '\$ORIGIN/../lib'] - ldflags.append('-Wl,-z,relro,-z,now -pie -s') + ldflags.append('-Wl,-z,relro,-z,now -pie') + if self.build_config.strip: + ldflags.append('-s') ldflags.append('-L%s' % os.path.join(llvm_path, 'lib')) cxxflags.append('-std=c++14') @@ -1479,7 +1493,7 @@ class LlvmPackage(BuildUtils): continue if bin_filename not in necessary_bin_files: os.remove(binary) - elif bin_filename not in script_bins: + elif bin_filename not in script_bins and self.build_config.strip: if bin_filename not in need_x_bins_darwin and self.host_is_darwin(): self.check_call(['strip', '-x', binary]) else: @@ -1489,7 +1503,7 @@ class LlvmPackage(BuildUtils): def strip_lldb_server(self, host, install_dir): clang_version_bin_dir = os.path.join(install_dir, 'lib', 'clang', self.build_config.CLANG_VERSION, 'bin') - if not host.startswith('linux') or not os.path.exists(clang_version_bin_dir): + if not host.startswith('linux') or not os.path.exists(clang_version_bin_dir) or not self.build_config.strip: return llvm_strip = os.path.join(install_dir, 'bin', 'llvm-strip') for llvm_triple_dir in os.listdir(clang_version_bin_dir): @@ -1693,6 +1707,16 @@ class LlvmPackage(BuildUtils): shutil.copyfile(os.path.join(self.build_config.LLVM_BUILD_DIR, "toolchain_readme.md"), os.path.join(install_dir, "README.md")) + # Generate manifest in install_dir + manifest = os.path.join(install_dir, 'manifest.xml') + repo_tool = os.path.join(self.build_config.REPOROOT_DIR, '.repo', 'repo', 'repo') + if os.path.isfile(repo_tool): + self.logger().info('Generating manifest.') + subprocess.run([repo_tool, 'manifest', '-r', '-o', manifest], shell=False, + stdout=subprocess.PIPE, cwd=self.build_config.REPOROOT_DIR) + else: + self.logger().error('Cannot generate manifest, repo tool not found.') + # Remove unnecessary binaries. necessary_bin_files = [] self.package_clang_bin_file(necessary_bin_files, ext)