diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -21,6 +21,7 @@ #include "DontModifyStdNamespaceCheck.h" #include "FloatLoopCounter.h" #include "LimitedRandomnessCheck.h" +#include "ObsolescentFunctionsCheck.h" #include "PostfixOperatorCheck.h" #include "ProperlySeededRandomGeneratorCheck.h" #include "SetLongJmpCheck.h" @@ -79,6 +80,8 @@ // ERR CheckFactories.registerCheck("cert-err34-c"); // MSC + CheckFactories.registerCheck( + "cert-obsolescent-functions"); CheckFactories.registerCheck("cert-msc30-c"); CheckFactories.registerCheck( "cert-msc32-c"); diff --git a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt @@ -6,6 +6,7 @@ DontModifyStdNamespaceCheck.cpp FloatLoopCounter.cpp LimitedRandomnessCheck.cpp + ObsolescentFunctionsCheck.cpp PostfixOperatorCheck.cpp ProperlySeededRandomGeneratorCheck.cpp SetLongJmpCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cert/ObsolescentFunctionsCheck.h b/clang-tools-extra/clang-tidy/cert/ObsolescentFunctionsCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cert/ObsolescentFunctionsCheck.h @@ -0,0 +1,36 @@ +//===--- ObsolescentFunctionsCheck.h - clang-tidy ---------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_OBSOLESCENTFUNCTIONSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_OBSOLESCENTFUNCTIONSCHECK_H + +#include "../ClangTidyCheck.h" +namespace clang { +namespace tidy { +namespace cert { + +/// Guards against using some unsafe function calls and function pointers which initialized with unsafe functions if some macros defined. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cert-obsolescent-functions.html +class ObsolescentFunctionsCheck : public ClangTidyCheck { +public: + ObsolescentFunctionsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + const std::pair UsingAnnexK(); + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpandedPP) override; +}; + +} // namespace cert +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_OBSOLESCENTFUNCTIONSCHECK_H diff --git a/clang-tools-extra/clang-tidy/cert/ObsolescentFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/cert/ObsolescentFunctionsCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cert/ObsolescentFunctionsCheck.cpp @@ -0,0 +1,103 @@ +//===--- ObsolescentFunctionsCheck.cpp - clang-tidy -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "ObsolescentFunctionsCheck.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cert { + +namespace { +static Preprocessor *PP; +} + +void ObsolescentFunctionsCheck::registerMatchers(MatchFinder *Finder) { + const auto CheckedFunctions = functionDecl(hasAnyName( + "::bsearch", "::fprintf", "::fscanf", "::fwprintf", "::fwscanf", + "::getenv", "::gmtime", "::localtime", "::mbsrtowcs", "::mbstowcs", + "::memcpy", "::memmove", "::printf", "::qsort", "::setbuf", "::snprintf", + "::sprintf", "::sscanf", "::strcat", "::strcpy", "::strerror", + "::strncat", "::strncpy", "::strtok", "::swprintf", "::swscanf", + "::vfprintf", "::vfscanf", "vfwprintf", "vfwscanf", "vprintf", "::vscanf", + "::vsnprintf", "::vsprintf", "::vsscanf", "::vswprintf", "::vswcanf", + "::wcrtomb", "::wcscat", "::wcscpy", "::wcsncat", "::wcsncpy", + "::wcsrtombs", "::wcstok", "::wcstombs", "::wctomb", "::wmemcpy", + "::wmemmove", "::wprintf", "::wscanf", "::strlen")); + Finder->addMatcher( + callExpr(callee(CheckedFunctions.bind("fn"))).bind("callexpr"), this); + + Finder->addMatcher(varDecl(hasInitializer(ignoringImpCasts(declRefExpr( + to(CheckedFunctions.bind("fnp")))))) + .bind("fp"), + this); +} + +void ObsolescentFunctionsCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *Preproc, + Preprocessor *ModuleExpandedPP) { + PP = Preproc; +} + +const std::pair ObsolescentFunctionsCheck::UsingAnnexK() { + bool AnnexKIsAvailable; + bool AnnexKIsWanted; + if (!PP->isMacroDefined("__STDC_LIB_EXT1__")) { + AnnexKIsAvailable = false; + } else { + AnnexKIsAvailable = true; + } + const IdentifierInfo *Id = PP->getIdentifierInfo("__STDC_WANT_LIB_EXT1__"); + AnnexKIsWanted = false; + if (!Id) { + AnnexKIsWanted = false; + } else { + const auto *MI = PP->getMacroInfo(Id); + const auto &T = MI->tokens().back(); + StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); + llvm::APInt IntValue; + ValueStr.getAsInteger(10, IntValue); + Optional AreSafeFunctionsWanted; + AreSafeFunctionsWanted = IntValue.getZExtValue(); + if (AreSafeFunctionsWanted.hasValue()) { + AnnexKIsWanted = AreSafeFunctionsWanted.getValue(); + } + } + return std::make_pair(AnnexKIsAvailable, AnnexKIsWanted); +} + +void ObsolescentFunctionsCheck::check(const MatchFinder::MatchResult &Result) { + std::pair AnnexKUsage = UsingAnnexK(); + bool AnnexKIsAvailable = std::get<0>(AnnexKUsage); + bool AnnexKIsWanted = std::get<1>(AnnexKUsage); + if (!AnnexKIsWanted || !AnnexKIsAvailable) { + return; + } + if (const auto *Function = Result.Nodes.getNodeAs("callexpr")) { + const FunctionDecl *FD = Result.Nodes.getNodeAs("fn"); + const NamedDecl *NFD = dyn_cast(FD); + std::string FunctionName = NFD->getNameAsString(); + diag(Function->getExprLoc(), "Unsafe function " + FunctionName + " used. " + + "Safe " + FunctionName + + "_s can be used instead."); + } + if (const auto *FunctionPointer = Result.Nodes.getNodeAs("fp")) { + if (const FunctionDecl *FD = Result.Nodes.getNodeAs("fnp")) { + const NamedDecl *NFD = dyn_cast(FD); + std::string FunctionName = NFD->getNameAsString(); + diag(FunctionPointer->getEndLoc(), + "Unsafe function " + FunctionName + " used. " + "Safe " + + FunctionName + "_s can be used instead."); + } + } +} +} // namespace cert +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyMiscModule + CountFunctionsCheck.cpp DefinitionsInHeadersCheck.cpp MiscTidyModule.cpp MisplacedConstCheck.cpp diff --git a/clang-tools-extra/clang-tidy/misc/CountFunctionsCheck.h b/clang-tools-extra/clang-tidy/misc/CountFunctionsCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/CountFunctionsCheck.h @@ -0,0 +1,147 @@ +//===--- CountFunctionsCheck.h - clang-tidy ---------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_COUNTFUNCTIONSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_COUNTFUNCTIONSCHECK_H + +#include "../ClangTidyCheck.h" +#include +#include +namespace clang { +namespace tidy { +namespace misc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-count-functions.html +class CountFunctionsCheck : public ClangTidyCheck { +private: + std::vector names; + std::map counts; +public: + CountFunctionsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) { + names = {"bsearch", + "fprintf", + + "fscanf", + + "fwprintf", + + "fwscanf", + + "getenv" + + "gmtime", + + "localtime", + + "mbsrtowcs", + + "mbstowcs", + + "memcpy", + + "memmove", + + "printf", + + "qsort", + + "setbuf", + + "snprintf", + + "sprintf", + + "sscanf", + + "strcat", + + "strcpy", + + "strerror", + + "strncat", + + "strncpy", + + "strtok", + + "swprintf", + + "swscanf", + + "vfprintf", + + "vfscanf", + + "vfwprintf", + + "vfwscanf", + + "vprintf", + + "vscanf", + + "vsnprintf", + + "vsprintf", + + "vsscanf", + + "vswprintf", + + "vswscanf", + + "vwprintf", + + "vwscanf", + + "wcrtomb" + + "wcscat", + + "wcscpy", + + "wcsncat", + + "wcsncpy", + + "wcsrtombs", + + "wcstok", + + "wcstombs", + + "wctomb", + + "wmemcpy", + + "wmemmove", + + "wprintf", + + "wscanf" + + }; + for(int i = 0; i < names.size(); ++i) { + counts[names[i]] = 0; + } + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + ~CountFunctionsCheck(); +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_COUNTFUNCTIONSCHECK_H diff --git a/clang-tools-extra/clang-tidy/misc/CountFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/misc/CountFunctionsCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/CountFunctionsCheck.cpp @@ -0,0 +1,55 @@ +//===--- CountFunctionsCheck.cpp - clang-tidy -----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "CountFunctionsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +void CountFunctionsCheck::registerMatchers(MatchFinder *Finder) { + // FIXME: Add matchers. + for(int i = 0; i < names.size(); ++i) { + Finder -> addMatcher(callExpr(callee(functionDecl(hasName("::" + names[i])))).bind(names[i]), this); + Finder -> addMatcher(varDecl( + hasInitializer( + ignoringImpCasts( + declRefExpr(to(functionDecl( + hasName("::" + names[i]))))))).bind(names[i] + "p"), this); + } +} + +void CountFunctionsCheck::check(const MatchFinder::MatchResult &Result) { + // FIXME: Add callback implementation. + for(int i = 0; i < names.size(); ++i) { + const CallExpr * sth = Result.Nodes.getNodeAs(names[i]); + if(sth != 0) { + ++counts[names[i]]; + } + const VarDecl * sth2 = Result.Nodes.getNodeAs(names[i]+"p"); + if(sth2 != 0) { + ++counts[names[i]]; + } + } +} + +CountFunctionsCheck::~CountFunctionsCheck() { + std::ofstream myfile; + myfile.open("counts.txt"); + for(int i = 0; i < names.size(); ++i) { + myfile << names[i] << ": " << counts[names[i]] << std::endl; + } + myfile.close(); +} +} // namespace misc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -194,6 +194,13 @@ against self-assignment either by checking self-assignment explicitly or using the copy-and-swap or the copy-and-move method. +- New :doc:`cert-obsolescent-functions + ` check. + + Guards against using some unsafe function calls and function pointers which + initialized with unsafe functions if some macros defined. + + - New :doc:`fuchsia-default-arguments-calls ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert-obsolescent-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-obsolescent-functions.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert-obsolescent-functions.rst @@ -0,0 +1,23 @@ +..title::clang-tidy-cert-obsolescent-functions + +cert-obsolescent-functions +========================== + +Guards against using some unsafe function calls and function pointers which initialized with unsafe functions if __STDC_LIB_EXT1__ macro is defined and the value of __STDC_WANT_LIB_EXT1__ is 1. The usage of following functions are checked : bsearch, + fprintf, fscanf, fwprintf, fwscanf, getenv, gmtime, localtime, mbsrtowcs, + mbstowcs, memcpy, memmove, printf, qsort, setbuf, snprintf, sprintf, sscanf, + strcat, strcpy, strerror, strncat, strncpy, strtok, swprintf, swscanf, + vfprintf, vfscanf, vfwprintf, vfwscanf, vprintf, vscanf vsnprintf, vspr, + wcscpy, wcsncat, wcsncpy wcsrtombs, wcstok, wcstombs, wctomb, wmemcpy, + wmemmove, wprintf, wscanf, + strlen + +This is a CERT security rule: +https://wiki.sei.cmu.edu/confluence/display/c/MSC24-C.+Do+not+use+deprecated+or+obsolescent+functions + Example : + .code-block:: + #define __STDC_WANT_LIB_EXT1__ 1 + int i = 2; + int j = 3; + memmove(&i, &j, sizeof(int)); // diagnosed if __STDC_LIB_EXT1__ is defined in a + // header, memmove_s usage is suggested instead. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -100,6 +100,7 @@ cert-msc32-c (redirects to cert-msc51-cpp) cert-msc50-cpp cert-msc51-cpp + cert-obsolescent-functions cert-oop11-cpp (redirects to performance-move-constructor-init) cert-oop54-cpp (redirects to bugprone-unhandled-self-assignment) cppcoreguidelines-avoid-c-arrays (redirects to modernize-avoid-c-arrays) diff --git a/clang-tools-extra/test/clang-tidy/cert-obsolescent-functions.cpp b/clang-tools-extra/test/clang-tidy/cert-obsolescent-functions.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/cert-obsolescent-functions.cpp @@ -0,0 +1,17 @@ +// RUN: %check_clang_tidy %s cert-obsolescent-functions %t -- + +#define __STDC_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +void * memmove(void *, void *, unsigned int); +void f1(const char *in) { + int i = 1; + int j = 2; + memmove(&i, &j, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Unsafe function memmove used. Safe memmove_s can be used instead. [cert-obsolescent-functions] +} + + +void f2(const char *in) { + void * (*func_ptr)(void *, void *, unsigned int) = memmove; +// CHECK-MESSAGES: :[[@LINE-1]]:56: warning: Unsafe function memmove used. Safe memmove_s can be used instead. [cert-obsolescent-functions] +}