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:
Aaron Ballman 2015-11-16 19:17:43 +00:00
parent c397b26790
commit 5a786ddf4c
7 changed files with 210 additions and 0 deletions

View File

@ -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");

View File

@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyCERTModule
CERTTidyModule.cpp
SetLongJmpCheck.cpp
ThrownExceptionTypeCheck.cpp
VariadicFunctionDefCheck.cpp
LINK_LIBS

View File

@ -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

View File

@ -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

View File

@ -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>`_.

View File

@ -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

View File

@ -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
}