forked from OSchip/llvm-project
Add a new clang-tidy checker that flags throw expressions whose thrown type is not nothrow copy constructible. While the compiler is free to elide copy constructor calls in some cases, it is under no obligation to do so, which makes the code a portability concern as well as a security concern.
This checker corresponds to the CERT secure coding rule: https://www.securecoding.cert.org/confluence/display/cplusplus/ERR60-CPP.+Exception+objects+must+be+nothrow+copy+constructible llvm-svn: 253246
This commit is contained in:
parent
c397b26790
commit
5a786ddf4c
|
@ -17,6 +17,7 @@
|
|||
#include "../misc/StaticAssertCheck.h"
|
||||
#include "../misc/ThrowByValueCatchByReferenceCheck.h"
|
||||
#include "SetLongJmpCheck.h"
|
||||
#include "ThrownExceptionTypeCheck.h"
|
||||
#include "VariadicFunctionDefCheck.h"
|
||||
|
||||
namespace clang {
|
||||
|
@ -40,6 +41,8 @@ public:
|
|||
// ERR
|
||||
CheckFactories.registerCheck<SetLongJmpCheck>(
|
||||
"cert-err52-cpp");
|
||||
CheckFactories.registerCheck<ThrownExceptionTypeCheck>(
|
||||
"cert-err60-cpp");
|
||||
CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
|
||||
"cert-err61-cpp");
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
|
|||
add_clang_library(clangTidyCERTModule
|
||||
CERTTidyModule.cpp
|
||||
SetLongJmpCheck.cpp
|
||||
ThrownExceptionTypeCheck.cpp
|
||||
VariadicFunctionDefCheck.cpp
|
||||
|
||||
LINK_LIBS
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//===--- ThrownExceptionTypeCheck.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 "ThrownExceptionTypeCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace {
|
||||
AST_MATCHER(CXXConstructorDecl, isNoThrowCopyConstructor) {
|
||||
if (!Node.isCopyConstructor())
|
||||
return false;
|
||||
|
||||
if (const auto *FnTy = Node.getType()->getAs<FunctionProtoType>())
|
||||
return FnTy->isNothrow(Node.getASTContext());
|
||||
llvm_unreachable("Copy constructor with no prototype");
|
||||
}
|
||||
} // end namespace
|
||||
|
||||
namespace tidy {
|
||||
void ThrownExceptionTypeCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
return;
|
||||
|
||||
Finder->addMatcher(
|
||||
cxxThrowExpr(
|
||||
has(cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
|
||||
isCopyConstructor(), unless(isNoThrowCopyConstructor()))))
|
||||
.bind("expr"))),
|
||||
this);
|
||||
|
||||
}
|
||||
|
||||
void ThrownExceptionTypeCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
|
||||
diag(E->getExprLoc(),
|
||||
"thrown exception type is not nothrow copy constructible");
|
||||
}
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
//===--- ThrownExceptionTypeCheck.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_CERT_THROWNEXCEPTIONTYPECHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
|
||||
/// Checks whether a thrown object is nothrow copy constructible.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/cert-thrown-exception-type.html
|
||||
class ThrownExceptionTypeCheck : public ClangTidyCheck {
|
||||
public:
|
||||
ThrownExceptionTypeCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
cert-err60-cpp
|
||||
==============
|
||||
|
||||
This check flags all throw expressions where the exception object is not nothrow
|
||||
copy constructible.
|
||||
|
||||
This check corresponds to the CERT C++ Coding Standard rule
|
||||
`ERR60-CPP. Exception objects must be nothrow copy constructible
|
||||
<https://www.securecoding.cert.org/confluence/display/cplusplus/ERR60-CPP.+Exception+objects+must+be+nothrow+copy+constructible>`_.
|
|
@ -3,6 +3,7 @@ List of clang-tidy Checks
|
|||
|
||||
.. toctree::
|
||||
cert-setlongjmp
|
||||
cert-thrown-exception-type
|
||||
cert-variadic-function-def
|
||||
cppcoreguidelines-pro-bounds-array-to-pointer-decay
|
||||
cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
// RUN: %check_clang_tidy %s cert-err60-cpp %t -- -- -std=c++11 -fcxx-exceptions
|
||||
|
||||
struct S {};
|
||||
struct T : S {};
|
||||
struct U {
|
||||
U() = default;
|
||||
U(const U&) = default;
|
||||
};
|
||||
|
||||
struct V {
|
||||
V() = default;
|
||||
V(const V&) noexcept;
|
||||
};
|
||||
|
||||
struct W {
|
||||
W() = default;
|
||||
W(const W&) noexcept(false);
|
||||
};
|
||||
|
||||
struct X {
|
||||
X() = default;
|
||||
X(const X&) {}
|
||||
};
|
||||
|
||||
struct Y {
|
||||
Y() = default;
|
||||
Y(const Y&) throw();
|
||||
};
|
||||
|
||||
struct Z {
|
||||
Z() = default;
|
||||
Z(const Z&) throw(int);
|
||||
};
|
||||
|
||||
void g() noexcept(false);
|
||||
|
||||
struct A {
|
||||
A() = default;
|
||||
A(const A&) noexcept(noexcept(g()));
|
||||
};
|
||||
|
||||
struct B {
|
||||
B() = default;
|
||||
B(const B&) = default;
|
||||
B(const A&) noexcept(false);
|
||||
};
|
||||
|
||||
class C {
|
||||
W M; // W is not no-throw copy constructible
|
||||
public:
|
||||
C() = default;
|
||||
C(const C&) = default;
|
||||
};
|
||||
|
||||
struct D {
|
||||
D() = default;
|
||||
D(const D&) noexcept(false);
|
||||
D(D&) noexcept(true);
|
||||
};
|
||||
|
||||
struct E {
|
||||
E() = default;
|
||||
E(E&) noexcept(true);
|
||||
E(const E&) noexcept(false);
|
||||
};
|
||||
|
||||
struct Allocates {
|
||||
int *x;
|
||||
Allocates() : x(new int(0)) {}
|
||||
Allocates(const Allocates &other) : x(new int(*other.x)) {}
|
||||
};
|
||||
|
||||
struct OptionallyAllocates {
|
||||
int *x;
|
||||
OptionallyAllocates() : x(new int(0)) {}
|
||||
OptionallyAllocates(const Allocates &other) noexcept(true) {
|
||||
try {
|
||||
x = new int(*other.x);
|
||||
} catch (...) {
|
||||
x = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void f() {
|
||||
throw 12; // ok
|
||||
throw "test"; // ok
|
||||
throw S(); // ok
|
||||
throw T(); // ok
|
||||
throw U(); // ok
|
||||
throw V(); // ok
|
||||
throw W(); // match, noexcept(false)
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible [cert-err60-cpp]
|
||||
throw X(); // match, no noexcept clause, nontrivial
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
|
||||
throw Y(); // ok
|
||||
throw Z(); // match, throw(int)
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
|
||||
throw A(); // match, noexcept(false)
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
|
||||
throw B(); // ok
|
||||
throw C(); // match, C has a member variable that makes it throwing on copy
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
|
||||
throw D(); // match, has throwing copy constructor
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
|
||||
throw E(); // match, has throwing copy constructor
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
|
||||
throw Allocates(); // match, copy constructor throws
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
|
||||
throw OptionallyAllocates(); // ok
|
||||
|
||||
}
|
Loading…
Reference in New Issue