diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 5862cfcac7e44c084ceea56063b52dd485fa97f7..2931942d09125401ed86eb22abfc83dae85a7421 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1774,6 +1774,18 @@ def SignalHandlerChecker : Checker<"SignalHandler">, ]>, Documentation; +def NapiScopeManagerChecker : Checker<"Napi.Scope">, + HelpText<"Check the use of napi_handle_scope">, + Documentation; + +def NapiGetArrBufferDataChecker : Checker<"Napi.GetArrayBuffer">, + HelpText<"Check the use of napi_get_arraybuffer_info">, + Documentation; + +def NapiWrapParamChecker : Checker<"Napi.Wrap">, + HelpText<"Check the use of napi_wrap">, + Documentation; + def RefBaseDeleteChecker : Checker<"RefBaseDelete">, HelpText<"Check for delete refbase managed by sptr/wptr">, Documentation; diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index bc2d51c800e161e8c46f777c3d65e8bddb099f2b..8b88cde528ef1a280a3a64bfa547946ce3928ab3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -135,13 +135,16 @@ add_clang_library(clangStaticAnalyzerCheckers WebKit/UncountedCallArgsChecker.cpp WebKit/UncountedLambdaCapturesChecker.cpp WebKit/UncountedLocalVarsChecker.cpp + + # OHOS_LOCAL begin + OpenHarmony/UnixAPIArgsChecker.cpp OpenHarmony/MemcpyChecker.cpp OpenHarmony/PrintSensitiveInfoChecker.cpp OpenHarmony/SignalHandlerChecker.cpp - - # OHOS_LOCAL begin - + OpenHarmony/NapiScopeManagerChecker.cpp + OpenHarmony/NapiGetArrBufferDataChecker.cpp + OpenHarmony/NapiWrapParamChecker.cpp OpenHarmony/RefBaseDeleteChecker.cpp OpenHarmony/OHPtrImplicitConversionChecker.cpp OpenHarmony/NotHeapObjChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiGetArrBufferDataChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiGetArrBufferDataChecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f892f27069eca8b432aaf510499a83f53a3a33a1 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiGetArrBufferDataChecker.cpp @@ -0,0 +1,144 @@ +//== NapiGetArrBufferDataChecker.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 NapiGetArrBufferDataChecker, which is a path-sensitive +// check looking for JS data is released by mistake. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" + +using namespace clang; +using namespace ento; +namespace { + +// Checker class +class NapiGetArrBufferDataChecker + : public Checker> { + // Constant definitions + constexpr static int NAPI_GET_ARRAYBUFFER_INFO_AYGS_NUM = 4; + constexpr static int FREE_AYGS_NUM = 1; + constexpr static int NAPI_DATA_POS = 2; + constexpr static int FREE_CONTENT_POS = 0; + + // Function wrapper class definitions + CallDescription NapiGetArrBuffer, Free; + + // BugType definitions + std::unique_ptr DataReleaseBugType; + + // Report warning function + void reportDataRelease(CheckerContext &C, const SymbolRef NapiSym) const; + +public: + NapiGetArrBufferDataChecker(); + + // Checker function + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + void checkPreStmt(const CXXDeleteExpr *DS, CheckerContext &C) const; +}; + +} // end anonymous namespace + +// Register set to save necessary data +REGISTER_SET_WITH_PROGRAMSTATE(DataSet, SymbolRef) + +// Constructor +NapiGetArrBufferDataChecker::NapiGetArrBufferDataChecker() + : NapiGetArrBuffer("napi_get_arraybuffer_info", + NAPI_GET_ARRAYBUFFER_INFO_AYGS_NUM), + Free("free", FREE_AYGS_NUM) { + // Initialize the bug types. + DataReleaseBugType.reset(new BugType( + this, "The third parameter illegal release", "OHOS Napi API Error")); +} + +// A callback after the function is executed to track the +// napi_get_arraybuffer_info +void NapiGetArrBufferDataChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + if (!this->NapiGetArrBuffer.matches(Call)) { + return; + } + ProgramStateRef State = C.getState(); + SVal S = Call.getArgSVal(NAPI_DATA_POS); + Optional X = S.getAs(); + StoreManager &SM = C.getStoreManager(); + SymbolRef DataSym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); + if (!DataSym) { + return; + } + State = State->add(DataSym); + C.addTransition(State); +} + +// A callback before the function is executed to track free +void NapiGetArrBufferDataChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!this->Free.matches(Call)) { + return; + } + ProgramStateRef State = C.getState(); + SymbolRef PtrSym = Call.getArgSVal(FREE_CONTENT_POS).getAsSymbol(); + if (!PtrSym) { + return; + } + if (State->contains(PtrSym)) { + State = State->remove(PtrSym); + reportDataRelease(C, PtrSym); + } +} + +// A callback before the delete statement is used to capture the delete +// behavior +void NapiGetArrBufferDataChecker::checkPreStmt(const CXXDeleteExpr *DE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef ArgSym = C.getSVal(DE->getArgument()).getAsSymbol(); + if (!ArgSym) { + return; + } + if (State->contains(ArgSym)) { + State = State->remove(ArgSym); + reportDataRelease(C, ArgSym); + } +} + +// Report if the data is released +void NapiGetArrBufferDataChecker::reportDataRelease( + CheckerContext &C, const SymbolRef NapiSym) const { + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState()); + if (!ErrNode) { + return; + } + auto R = std::make_unique( + *DataReleaseBugType, "The variable is not allowed to be released", + ErrNode); + R->markInteresting(NapiSym); + C.emitReport(std::move(R)); +} + +void ento::registerNapiGetArrBufferDataChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterNapiGetArrBufferDataChecker( + const CheckerManager &mgr) { + return true; +} \ No newline at end of file diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiScopeManagerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiScopeManagerChecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..641e93aa00a19acd2056b418bfce2896e0e65698 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiScopeManagerChecker.cpp @@ -0,0 +1,398 @@ +//== NapiScopeManagerChecker.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 NapiScopeManagerChecker, which is a path-sensitive check +// looking for Memory leak issues due to no napi_handle_scope is used. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "llvm/ADT/StringRef.h" +#include + +using namespace clang; +using namespace ento; +namespace { +using SymbolVector = SmallVector; + +// String packaging class +class StringWarpper { + const std::string Str; + +public: + StringWarpper(const std::string &S) : Str(S) {} + const std::string &get() { return Str; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddString(Str); } + bool operator==(const StringWarpper &rhs) const { return Str == rhs.Str; } + bool operator!=(const StringWarpper &rhs) const { return Str != rhs.Str; } + bool operator<(const StringWarpper &rhs) const { return Str < rhs.Str; } +}; + +// napi_handle_scope state class +struct NapiScopeManagerState { +private: + enum Kind { Opened, Closed } K; + NapiScopeManagerState(Kind InK) : K(InK) {} + +public: + bool isOpened() const { return K == Opened; } + bool isClosed() const { return K == Closed; } + + static NapiScopeManagerState getOpened() { + return NapiScopeManagerState(Opened); + } + static NapiScopeManagerState getClosed() { + return NapiScopeManagerState(Closed); + } + + bool operator==(const NapiScopeManagerState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; + +// uv_queue_work callback state class +struct UvQueueWorkCallBackState { +private: + enum Kind { Safe, Dangerous } K; + UvQueueWorkCallBackState(Kind InK) : K(InK) {} + +public: + bool isSafe() const { return K == Safe; } + bool isDangerous() const { return K == Dangerous; } + + static UvQueueWorkCallBackState getSafe() { + return UvQueueWorkCallBackState(Safe); + } + static UvQueueWorkCallBackState getDangerous() { + return UvQueueWorkCallBackState(Dangerous); + } + + bool operator==(const UvQueueWorkCallBackState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; + +// Checker class +class NapiScopeManagerChecker + : public Checker> { + // Constant definitions + constexpr static int NAPI_SCOPE_AYGS_NUM = 2; + constexpr static int NAPI_SCOPE_HANDLE_POS = 1; + constexpr static int UV_QUEUE_WORK_CALLBACK_AYGS_NUM = 2; + + // Function wrapper class definitions + CallDescription OpenHandleScope, CloseHandleScope; + + // BugType definitions + std::unique_ptr DoubleCloseBugType; + std::unique_ptr LeakBugType; + std::unique_ptr UvLeakBugType; + + // Report warning functions + + void reportDoubleClose(SymbolRef NapiDescSym, const CallEvent &Call, + CheckerContext &C) const; + + void reportLeaks(ArrayRef LeakedStreams, CheckerContext &C, + ExplodedNode *ErrNode) const; + + void reportUvCallBackLeaks(CheckerContext &C, const SourceRange &SR) const; + + // Tool functions + + void UvCallbackDetect(CheckerContext &C, const bool &isBegin) const; + + bool isLeaked(SymbolRef Sym, const NapiScopeManagerState &SS, bool IsSymDead, + ProgramStateRef State) const; + +public: + NapiScopeManagerChecker(); + + // Checker functions + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + + void checkBeginFunction(CheckerContext &C) const; + + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + + void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; +}; + +} // end anonymous namespace + +// Register map to save necessary data +REGISTER_MAP_WITH_PROGRAMSTATE(HandleScopeMap, SymbolRef, NapiScopeManagerState) +REGISTER_MAP_WITH_PROGRAMSTATE(UvQueueWorkMap, StringWarpper, + UvQueueWorkCallBackState) + +// Constructor +NapiScopeManagerChecker::NapiScopeManagerChecker() + : OpenHandleScope("napi_open_handle_scope", NAPI_SCOPE_AYGS_NUM), + CloseHandleScope("napi_close_handle_scope", NAPI_SCOPE_AYGS_NUM) { + // Initialize the bug types. + DoubleCloseBugType.reset(new BugType(this, "Double close 'napi_handle_scope'", + "OHOS Napi API Error")); + + LeakBugType.reset(new BugType(this, "Resource 'napi_handle_scope' Leaked", + "OHOS Napi API Error", + /*SuppressOnSink=*/false)); + + UvLeakBugType.reset(new BugType( + this, "The callback of uv_queue_work no use 'napi_handle_scope'", + "OHOS Napi API Error")); +} + +// Determine whether napi_handle_scope memory leak +bool NapiScopeManagerChecker::isLeaked(SymbolRef Sym, + const NapiScopeManagerState &SS, + bool IsSymDead, + ProgramStateRef State) const { + if (IsSymDead && SS.isOpened()) { + ConstraintManager &CMgr = State->getConstraintManager(); + ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym); + return !OpenFailed.isConstrainedTrue(); + } + return false; +} + +// A callback function that determines whether it is uv_queue_work and stores +// the status in Map +void NapiScopeManagerChecker::UvCallbackDetect(CheckerContext &C, + const bool &isBegin) const { + const FunctionDecl *checkFun = + C.getCurrentAnalysisDeclContext()->getDecl()->getAsFunction(); + if (checkFun->getNumParams() != UV_QUEUE_WORK_CALLBACK_AYGS_NUM) { + return; + } + std::string param1Type = + checkFun->getParamDecl(0)->getOriginalType().getAsString(); + std::string param2Type = + checkFun->getParamDecl(1)->getOriginalType().getAsString(); + if (param1Type != "uv_work_t *") { + return; + } + if ((param2Type != "int32_t" && param2Type != "int")) { + return; + } + + ProgramStateRef State = C.getState(); + StringWarpper FunName = StringWarpper(checkFun->getNameAsString()); + if (isBegin) { + State = State->set( + FunName, UvQueueWorkCallBackState::getDangerous()); + } else { + State = State->remove(FunName); + } + C.addTransition(State); +} + +// A callback before the function is executed to track the +// napi_close_handle_scope +void NapiScopeManagerChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!this->CloseHandleScope.matches(Call)) { + return; + } + ProgramStateRef State = C.getState(); + SymbolRef Napisym = Call.getArgSVal(NAPI_SCOPE_HANDLE_POS).getAsSymbol(); + if (!Napisym) { + return; + } + + const NapiScopeManagerState *NS = State->get(Napisym); + if (!NS) { + return; + } + + if (NS->isClosed()) { + reportDoubleClose(Napisym, Call, C); + return; + } + + State = + State->set(Napisym, NapiScopeManagerState::getClosed()); + C.addTransition(State); +} + +// A callback after the function is executed to track the napi_open_handle_scope +void NapiScopeManagerChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + if (!this->OpenHandleScope.matches(Call)) { + return; + } + ProgramStateRef State = C.getState(); + UvQueueWorkMapTy CBS = State->get(); + if (CBS.isEmpty()) + return; + for (auto cb : CBS) { + if (cb.second.isDangerous()) { + State = State->set(cb.first, + UvQueueWorkCallBackState::getSafe()); + } + } + + SVal ScopeSVal = Call.getArgSVal(NAPI_SCOPE_HANDLE_POS); + Optional X = ScopeSVal.getAs(); + StoreManager &SM = C.getStoreManager(); + SymbolRef NapiSym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); + if (!NapiSym) { + return; + } + + State = + State->set(NapiSym, NapiScopeManagerState::getOpened()); + + SVal StatusVal = Call.getReturnValue(); + Optional Dval = StatusVal.getAs(); + if (!Dval) { + return; + } + State = State->assume(*Dval, false); + C.addTransition(State); +} + +// A callbacks for symbol death, determine whether the scope has died +void NapiScopeManagerChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolVector LeakedStreams; + HandleScopeMapTy TrackedStreams = State->get(); + for (HandleScopeMapTy::iterator I = TrackedStreams.begin(), + E = TrackedStreams.end(); + I != E; ++I) { + SymbolRef Sym = I->first; + + bool IsSymDead = SymReaper.isDead(Sym); + + if (isLeaked(Sym, I->second, IsSymDead, State)) { + LeakedStreams.push_back(Sym); + } + + if (IsSymDead) { + State = State->remove(Sym); + } + } + + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) { + return; + } + reportLeaks(LeakedStreams, C, N); +} + +// A callback from which the function starts, which tracks the start of the +// callback function of uv_queue_work +void NapiScopeManagerChecker::checkBeginFunction(CheckerContext &C) const { + UvCallbackDetect(C, true); +} + +// A callback from which the function ends, which tracks the end of the callback function of +// uv_queue_work +void NapiScopeManagerChecker::checkEndFunction(const ReturnStmt *RS, + CheckerContext &C) const { + UvCallbackDetect(C, false); +} + +// A callback before the declaration statement is used to capture the JSValue +// declaration +void NapiScopeManagerChecker::checkPreStmt(const DeclStmt *DS, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + UvQueueWorkMapTy CBS = State->get(); + if (CBS.isEmpty()) { + return; + } + + if (!DS->isSingleDecl()) { + return; + } + + const VarDecl *VD = dyn_cast(DS->getSingleDecl()); + if (!VD) { + return; + } + + const std::string DeclType = VD->getType().getAsString(); + if (DeclType.find("napi_value") == std::string::npos) { + return; + } + + for (auto cb : CBS) { + if (cb.second.isDangerous()) { + + reportUvCallBackLeaks(C, DS->getSourceRange()); + } + } +} + +// Report if the scope is open but not closed +void NapiScopeManagerChecker::reportLeaks(ArrayRef LeakedStreams, + CheckerContext &C, + ExplodedNode *ErrNode) const { + for (SymbolRef LeakedStream : LeakedStreams) { + auto R = std::make_unique( + *LeakBugType, "Opened Napi is never closed; potential resource leak", + ErrNode); + R->markInteresting(LeakedStream); + C.emitReport(std::move(R)); + } +} + +// Report if the scope is double closed +void NapiScopeManagerChecker::reportDoubleClose(SymbolRef NapiSym, + const CallEvent &Call, + CheckerContext &C) const { + ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); + if (!ErrNode) { + return; + } + + auto R = std::make_unique( + *DoubleCloseBugType, "Closing a previously closed napi_handle_scope", + ErrNode); + R->addRange(Call.getSourceRange()); + R->markInteresting(NapiSym); + C.emitReport(std::move(R)); +} + +// Report if the callback of uv_queue_work use JSValue but no use napi_handle_scope +void NapiScopeManagerChecker::reportUvCallBackLeaks( + CheckerContext &C, const SourceRange &SR) const { + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState()); + if (!ErrNode) { + return; + } + + auto R = std::make_unique( + *UvLeakBugType, + "Illegal access to JSValue, access to JSValue must be within the napi " + "handle scope", + ErrNode); + R->addRange(SR); + C.emitReport(std::move(R)); +} + +void ento::registerNapiScopeManagerChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterNapiScopeManagerChecker(const CheckerManager &mgr) { + return true; +} \ No newline at end of file diff --git a/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiWrapParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiWrapParamChecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b82e68e527f15960399013c344f65c9a50369cbe --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OpenHarmony/NapiWrapParamChecker.cpp @@ -0,0 +1,101 @@ +//== NapiWrapParamChecker.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 NapiWrapParamChecker, which is a path-sensitive check +// looking for the function return args is not null. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" + +using namespace clang; +using namespace ento; +namespace { + +// Checker class +class NapiWrapParamChecker : public Checker { + // Constant definitions + constexpr static int NAPI_WRAP_AYGS_NUM = 6; + constexpr static int LAST_ARG_POS = 5; + + // Function wrapper class definitions + CallDescription NapiWrap; + + // BugType definitions + std::unique_ptr LastParamNotNullBugType; + + // Report warning function + void reportParamNotNull(const CallEvent &Call, CheckerContext &C, + const SymbolRef NapiSym) const; + +public: + NapiWrapParamChecker(); + + // Checker function + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +}; + +} // end anonymous namespace + +// Constructor +NapiWrapParamChecker::NapiWrapParamChecker() + : NapiWrap("napi_wrap", NAPI_WRAP_AYGS_NUM) { + // Initialize the bug types. + LastParamNotNullBugType.reset(new BugType( + this, "last parameter of 'napi_wrap' not null", "OHOS Napi API Error")); +} + +// A callback before the function is executed to track napi_wrap +void NapiWrapParamChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!this->NapiWrap.matches(Call)) { + return; + } + ProgramStateRef State = C.getState(); + SVal S = Call.getArgSVal(LAST_ARG_POS); + Optional NapiSVal = S.getAs(); + if (!NapiSVal) { + return; + } + ConditionTruthVal Nullness = State->isNull(*NapiSVal); + if (Nullness.isConstrainedFalse()) { + reportParamNotNull(Call, C, S.getAsSymbol()); + } +} + +// Report if the third args of napi_wrap is not null +void NapiWrapParamChecker::reportParamNotNull(const CallEvent &Call, + CheckerContext &C, + const SymbolRef NapiSym) const { + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState()); + if (!ErrNode) { + return; + } + + auto R = std::make_unique( + *LastParamNotNullBugType, "The last parameter of napi_wrap is not null", + ErrNode); + R->addRange(Call.getSourceRange()); + R->markInteresting(NapiSym); + C.emitReport(std::move(R)); +} + +void ento::registerNapiWrapParamChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterNapiWrapParamChecker(const CheckerManager &mgr) { + return true; +} \ No newline at end of file diff --git a/clang/test/Analysis/napi_get_arraybuffer_info_test.cpp b/clang/test/Analysis/napi_get_arraybuffer_info_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..883ddbdf67455ca6bdeb9b15f4b9f8681da30155 --- /dev/null +++ b/clang/test/Analysis/napi_get_arraybuffer_info_test.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// 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 +//===----------------------------------------------------------------------===// + +// RUN: %clang --analyze -Xanalyzer -analyzer-checker=openharmony %s + +#include "napi_include.h" +#include +#include + +void test_bad_case_1() { + napi_env env = nullptr; + napi_value Args = nullptr; + void *data = nullptr; + size_t len = 0; + napi_get_arraybuffer_info(env, Args, &data, &len); + delete data; // expected-warning{{The variable is not allowed to be released}} +} + +void test_bad_case_2() { + napi_env env = nullptr; + napi_value Args = nullptr; + void *data = nullptr; + size_t len = 0; + napi_get_arraybuffer_info(env, Args, &data, &len); + free(data); // expected-warning{{The variable is not allowed to be released}} +} + +void test_good_case_1() { + napi_env env = nullptr; + napi_value Args = nullptr; + void *data = nullptr; + size_t len = 0; + napi_get_arraybuffer_info(env, Args, &data, &len); // no-warning +} diff --git a/clang/test/Analysis/napi_handle_scope_test.cpp b/clang/test/Analysis/napi_handle_scope_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d69ad138f1f2181b468eef36a85e999be5c0fee --- /dev/null +++ b/clang/test/Analysis/napi_handle_scope_test.cpp @@ -0,0 +1,180 @@ +//===----------------------------------------------------------------------===// +// 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 +//===----------------------------------------------------------------------===// + +// RUN: %clang --analyze -Xanalyzer -analyzer-checker=openharmony %s + +#include "napi_include.h" + +void test_bad_case_1() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_value jsCallback = nullptr; // expected-warning{{Illegal access to JSValue, access to JSValue must be within the napi handle scope}} + jsCallback = 0; + delete work; + }); +} + +void test_bad_case_2() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_value args[1] = {nullptr}; // expected-warning{{Illegal access to JSValue, access to JSValue must be within the napi handle scope}} + args[0] = 0; + delete work; + }); +} + +void test_bad_case_3() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + napi_value jsCallback = nullptr; // expected-warning{{Opened Napi is never closed; potential resource leak}} + jsCallback = 0; + delete work; + }); +} + +void test_bad_case_4() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + napi_value args[1] = {nullptr}; // expected-warning{{Opened Napi is never closed; potential resource leak}} + args[0] = 0; + delete work; + }); +} + +void test_bad_case_5() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + delete work; // expected-warning{{Opened Napi is never closed; potential resource leak}} + }); +} + +void test_bad_case_6() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + napi_value jsCallback = nullptr; + jsCallback = 0; + napi_close_handle_scope(env_, scope); + napi_close_handle_scope(env_, scope); // expected-warning{{Closing a previously closed napi_handle_scope}} + delete work; + }); +} + +void test_bad_case_7() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + napi_value args[1] = {nullptr}; + args[0] = 0; + napi_close_handle_scope(env_, scope); + napi_close_handle_scope(env_, scope); // expected-warning{{Closing a previously closed napi_handle_scope}} + delete work; + }); +} + +void test_good_case_1() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + napi_value jsCallback = nullptr; + jsCallback = 0; + napi_close_handle_scope(env_, scope); + delete work; + }); +} + +void test_good_case_2() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + napi_value args[1] = {nullptr}; + args[0] = 0; + napi_close_handle_scope(env_, scope); + delete work; + }); +} + +void test_good_case_3() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_status stat = napi_open_handle_scope(env_, &scope); + if (stat != napi_ok) { + return; + } + napi_value args[1] = {nullptr}; + args[0] = 0; + napi_close_handle_scope(env_, scope); + delete work; + }); +} + +void test_good_case_4() { + uv_loop_s *loop = nullptr; + uv_work_t *work = new uv_work_t; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + napi_env env_ = nullptr; + napi_handle_scope scope = nullptr; + napi_open_handle_scope(env_, &scope); + if (scope == nullptr) { + return; + } + napi_value args[1] = {nullptr}; + args[0] = 0; + napi_close_handle_scope(env_, scope); + delete work; + }); +} \ No newline at end of file diff --git a/clang/test/Analysis/napi_include.h b/clang/test/Analysis/napi_include.h new file mode 100644 index 0000000000000000000000000000000000000000..dcac71463f38a93558e059845219a3cc80871f25 --- /dev/null +++ b/clang/test/Analysis/napi_include.h @@ -0,0 +1,84 @@ +#include + +typedef struct napi_env__* napi_env; +typedef struct napi_value__* napi_value; +typedef struct napi_ref__* napi_ref; +typedef struct napi_handle_scope__* napi_handle_scope; + +typedef enum { + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_string_expected, + napi_name_expected, + napi_function_expected, + napi_number_expected, + napi_boolean_expected, + napi_array_expected, + napi_generic_failure, + napi_pending_exception, + napi_cancelled, + napi_escape_called_twice, + napi_handle_scope_mismatch, + napi_callback_scope_mismatch, + napi_queue_full, + napi_closing, + napi_bigint_expected, + napi_date_expected, + napi_arraybuffer_expected, + napi_detachable_arraybuffer_expected, + napi_would_deadlock // unused +} napi_status; + +typedef void (*napi_finalize)(napi_env env, + void* finalize_data, + void* finalize_hint); + +napi_status napi_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); + +napi_status napi_open_handle_scope(napi_env env, + napi_handle_scope* result); +napi_status napi_close_handle_scope(napi_env env, + napi_handle_scope scope); +napi_status napi_get_arraybuffer_info(napi_env env, + napi_value arraybuffer, + void** data, + size_t* byte_length); + + + + +typedef struct uv_loop_s uv_loop_t; +typedef struct uv_work_s uv_work_t; +typedef void (*uv_work_cb)(uv_work_t* req); +typedef void (*uv_after_work_cb)(uv_work_t* req, int status); +struct uv_work_s { + uv_loop_t* loop; + uv_work_cb work_cb; + uv_after_work_cb after_work_cb; +}; + + int uv_queue_work(uv_loop_t* loop, + uv_work_t* req, + uv_work_cb work_cb, + uv_after_work_cb after_work_cb); +struct uv_loop_s { + /* User data - use this for whatever. */ + void* data; + /* Loop reference counting. */ + unsigned int active_handles; + void* handle_queue[2]; + union { + void* unused; + unsigned int count; + } active_reqs; + /* Internal storage for future extensions. */ + void* internal_fields; + /* Internal flag to signal loop stop. */ + unsigned int stop_flag; +}; diff --git a/clang/test/Analysis/napi_wrap_test.cpp b/clang/test/Analysis/napi_wrap_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56c8878874226bbf559b9d4aeed6509efa1e780e --- /dev/null +++ b/clang/test/Analysis/napi_wrap_test.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// 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 +//===----------------------------------------------------------------------===// + +// RUN: %clang --analyze -Xanalyzer -analyzer-checker=openharmony %s + +#include "napi_include.h" + +void test_bad_case_1() { + napi_env env = nullptr; + napi_value thisArg = nullptr; + void* cbInfo = nullptr; + napi_ref* res = new napi_ref[5]; + napi_wrap(env, thisArg, cbInfo, [](napi_env env, void* data, void* hint) {}, nullptr, res); // expected-warning{{The last parameter of napi_wrap is not null}} +} + +void test_good_case_1() { + napi_env env = nullptr; + napi_value thisArg = nullptr; + void* cbInfo = nullptr; + napi_wrap(env, thisArg, cbInfo, [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); +} \ No newline at end of file