aaron.ballman created this revision.
aaron.ballman added reviewers: alexfh, sbenza.
aaron.ballman added a subscriber: cfe-commits.

setjmp and longjmp facilities in C++ are unsafe due to the likelihood of 
triggering undefined behavior with nontrivial type destruction, etc. Instead, 
exception handling facilities are a better choice. This patch adds a checker 
that discourages the use of setjmp and longjmp in C++.

This patch corresponds to CERT C++ Coding Standard rule: 
https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=1834

http://reviews.llvm.org/D13567

Files:
  clang-tidy/cert/CERTTidyModule.cpp
  clang-tidy/cert/CMakeLists.txt
  clang-tidy/cert/SetLongJmpCheck.cpp
  clang-tidy/cert/SetLongJmpCheck.h
  docs/clang-tidy/checks/cert-setlongjmp.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/cert-setlongjmp.cpp

Index: test/clang-tidy/cert-setlongjmp.cpp
===================================================================
--- test/clang-tidy/cert-setlongjmp.cpp
+++ test/clang-tidy/cert-setlongjmp.cpp
@@ -0,0 +1,26 @@
+// RUN: %python %S/check_clang_tidy.py %s cert-err52-cpp %t -- -std=c++11
+
+typedef void *jmp_buf;
+extern int __setjmpimpl(jmp_buf);
+#define setjmp(x) __setjmpimpl(x)
+[[noreturn]] extern void longjmp(jmp_buf, int);
+
+namespace std {
+using ::jmp_buf;
+using ::longjmp;
+}
+
+static jmp_buf env;
+void g() {
+  std::longjmp(env, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead [cert-err52-cpp]
+  ::longjmp(env, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead
+  longjmp(env, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead
+}
+
+void f() {
+  (void)setjmp(env);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not call 'setjmp'; consider using exception handling instead
+}
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -2,6 +2,7 @@
 =========================
 
 .. toctree::
+   cert-setlongjmp
    cert-variadic-function-def
    cppcoreguidelines-pro-type-const-cast
    cppcoreguidelines-pro-type-reinterpret-cast
Index: docs/clang-tidy/checks/cert-setlongjmp.rst
===================================================================
--- docs/clang-tidy/checks/cert-setlongjmp.rst
+++ docs/clang-tidy/checks/cert-setlongjmp.rst
@@ -0,0 +1,11 @@
+cert-err52-cpp
+==============
+
+The C standard library facilities setjmp() and longjmp() can be used to
+simulate throwing and catching exceptions. However, these facilities bypass
+automatic resource management and can result in undefined behavior, commonly
+including resource leaks, and denial-of-service attacks.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR52-CPP. Do not use setjmp() or longjmp()
+<https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=1834>`_.
Index: clang-tidy/cert/SetLongJmpCheck.h
===================================================================
--- clang-tidy/cert/SetLongJmpCheck.h
+++ clang-tidy/cert/SetLongJmpCheck.h
@@ -0,0 +1,37 @@
+//===--- SetLongJmpCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+
+/// Guards against use of setjmp/longjmp in C++ code
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-setlongjmp.html
+class SetLongJmpCheck : public ClangTidyCheck {
+public:
+  SetLongJmpCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+  static const char DiagWording[];
+};
+
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
+
Index: clang-tidy/cert/SetLongJmpCheck.cpp
===================================================================
--- clang-tidy/cert/SetLongJmpCheck.cpp
+++ clang-tidy/cert/SetLongJmpCheck.cpp
@@ -0,0 +1,80 @@
+//===--- SetLongJmpCheck.cpp - clang-tidy----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SetLongJmpCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+
+const char SetLongJmpCheck::DiagWording[] =
+    "do not call %0; consider using exception handling instead";
+
+namespace {
+class SetJmpMacroCallbacks : public PPCallbacks {
+  Preprocessor *PP;
+  SetLongJmpCheck &Check;
+
+public:
+  explicit SetJmpMacroCallbacks(Preprocessor *PP, SetLongJmpCheck &Check)
+      : PP(PP), Check(Check) {}
+
+  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+                    SourceRange Range, const MacroArgs *Args) override {
+    const auto *II = MacroNameTok.getIdentifierInfo();
+    if (!II)
+      return;
+
+    if (II->getName() == "setjmp")
+      Check.diag(Range.getBegin(), Check.DiagWording) << II;
+  }
+};
+} // namespace
+
+void SetLongJmpCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  // This checker only applies to C++, where exception handling is a superior
+  // solution to setjmp/longjmp calls.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Per [headers]p5, setjmp must be exposed as a macro instead of a function,
+  // despite the allowance in C for setjmp to also be an extern function.
+  Compiler.getPreprocessor().addPPCallbacks(
+      llvm::make_unique<SetJmpMacroCallbacks>(&Compiler.getPreprocessor(),
+                                              *this));
+}
+
+void SetLongJmpCheck::registerMatchers(MatchFinder *Finder) {
+  // This checker only applies to C++, where exception handling is a superior
+  // solution to setjmp/longjmp calls.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // In case there is an implementation that happens to define setjmp as a
+  // function instead of a macro, this will also catch use of it. However, we
+  // are primarily searching for uses of longjmp.
+  Finder->addMatcher(callExpr(callee(functionDecl(anyOf(hasName("setjmp"),
+                                                        hasName("longjmp")))))
+                         .bind("expr"),
+                     this);
+}
+
+void SetLongJmpCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<CallExpr>("expr");
+  diag(E->getExprLoc(), DiagWording) << cast<NamedDecl>(E->getCalleeDecl());
+}
+
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/cert/CMakeLists.txt
===================================================================
--- clang-tidy/cert/CMakeLists.txt
+++ clang-tidy/cert/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_clang_library(clangTidyCERTModule
   CERTTidyModule.cpp
+  SetLongJmpCheck.cpp
   VariadicFunctionDefCheck.cpp
 
   LINK_LIBS
Index: clang-tidy/cert/CERTTidyModule.cpp
===================================================================
--- clang-tidy/cert/CERTTidyModule.cpp
+++ clang-tidy/cert/CERTTidyModule.cpp
@@ -15,6 +15,7 @@
 #include "../misc/NewDeleteOverloadsCheck.h"
 #include "../misc/NonCopyableObjects.h"
 #include "../misc/StaticAssertCheck.h"
+#include "SetLongJmpCheck.h"
 #include "VariadicFunctionDefCheck.h"
 
 namespace clang {
@@ -35,6 +36,9 @@
     // OOP
     CheckFactories.registerCheck<MoveConstructorInitCheck>(
         "cert-oop11-cpp");
+    // ERR
+    CheckFactories.registerCheck<SetLongJmpCheck>(
+        "cert-err52-cpp");
 
     // C checkers
     // DCL
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to