Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -40,6 +40,7 @@ SuspiciousStringCompareCheck.cpp SwappedArgumentsCheck.cpp ThrowByValueCatchByReferenceCheck.cpp + UndefinedMemoryManipulationCheck.cpp UndelegatedConstructor.cpp UniqueptrResetReleaseCheck.cpp UnusedAliasDeclsCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -48,6 +48,7 @@ #include "SwappedArgumentsCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" #include "UnconventionalAssignOperatorCheck.h" +#include "UndefinedMemoryManipulationCheck.h" #include "UndelegatedConstructor.h" #include "UniqueptrResetReleaseCheck.h" #include "UnusedAliasDeclsCheck.h" @@ -132,6 +133,8 @@ "misc-swapped-arguments"); CheckFactories.registerCheck( "misc-throw-by-value-catch-by-reference"); + CheckFactories.registerCheck( + "misc-undefined-memory-manipulation"); CheckFactories.registerCheck( "misc-undelegated-constructor"); CheckFactories.registerCheck( Index: clang-tidy/misc/UndefinedMemoryManipulationCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/UndefinedMemoryManipulationCheck.h @@ -0,0 +1,37 @@ +//===--- UndefinedMemoryManipulationCheck.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_MISC_UNDEFINED_MEMORY_MANIPULATION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDEFINED_MEMORY_MANIPULATION_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and +/// ``memmove()`` on not TriviallyCopyable objects resulting in undefined +/// behavior. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-undefined-memory-manipulation.html +class UndefinedMemoryManipulationCheck : public ClangTidyCheck { +public: + UndefinedMemoryManipulationCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDEFINED_MEMORY_MANIPULATION_H Index: clang-tidy/misc/UndefinedMemoryManipulationCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/UndefinedMemoryManipulationCheck.cpp @@ -0,0 +1,61 @@ +//===--- UndefinedMemoryManipulationCheck.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 "UndefinedMemoryManipulationCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +namespace { +AST_MATCHER(CXXRecordDecl, isNotTriviallyCopyable) { + return !(Node.isTriviallyCopyable()); +} +} // namespace + +void UndefinedMemoryManipulationCheck::registerMatchers(MatchFinder *Finder) { + const auto NotTriviallyCopyableObject = + hasType(pointsTo(cxxRecordDecl(isNotTriviallyCopyable()))); + + // Check whether destination object is not TriviallyCopyable. + // Applicable to all three memory manipulation functions. + Finder->addMatcher(callExpr(callee(functionDecl(hasAnyName( + "::memset", "::memcpy", "::memmove"))), + hasArgument(0, NotTriviallyCopyableObject)) + .bind("dest"), + this); + + // Check whether source object is not TriviallyCopyable. + // Only applicable to memcpy() and memmove(). + Finder->addMatcher( + callExpr(callee(functionDecl(hasAnyName("::memcpy", "::memmove"))), + hasArgument(1, NotTriviallyCopyableObject)) + .bind("src"), + this); +} + +void UndefinedMemoryManipulationCheck::check( + const MatchFinder::MatchResult &Result) { + if (const auto *Destination = Result.Nodes.getNodeAs("dest")) { + diag(Destination->getLocStart(), "undefined behavior, destination " + "object is not TriviallyCopyable"); + } + if (const auto *Source = Result.Nodes.getNodeAs("src")) { + diag(Source->getLocStart(), "undefined behavior, source object is not " + "TriviallyCopyable"); + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -97,6 +97,12 @@ Finds uses of ``__func__`` or ``__FUNCTION__`` inside lambdas. +- New `misc-undefined-memory-manipulation + `_ check + + Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and ``memmove()`` + on not TriviallyCopyable objects resulting in undefined behavior. + - New `modernize-replace-random-shuffle `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -112,6 +112,7 @@ misc-swapped-arguments misc-throw-by-value-catch-by-reference misc-unconventional-assign-operator + misc-undefined-memory-manipulation misc-undelegated-constructor misc-uniqueptr-reset-release misc-unused-alias-decls Index: docs/clang-tidy/checks/misc-undefined-memory-manipulation.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-undefined-memory-manipulation.rst @@ -0,0 +1,7 @@ +.. title:: clang-tidy - misc-undefined-memory-manipulation + +misc-undefined-memory-manipulation +================================== + +Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and +``memmove()`` on not TriviallyCopyable objects resulting in undefined behavior. Index: test/clang-tidy/misc-undefined-memory-manipulation.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-undefined-memory-manipulation.cpp @@ -0,0 +1,178 @@ +// RUN: %check_clang_tidy %s misc-undefined-memory-manipulation %t + +void *memset(void *, int, __SIZE_TYPE__); +void *memcpy(void *, const void *, __SIZE_TYPE__); +void *memmove(void *, const void *, __SIZE_TYPE__); + +namespace std { +using ::memcpy; +using ::memmove; +using ::memset; +} + +// TriviallyCopyable types: +struct Plain { + int n; +}; + +enum E { + X, + Y, + Z +}; + +struct Base { + float b; +}; + +struct Derived : Base { + bool d; +}; + +// not TriviallyCopyable types: +struct Destruct { + ~Destruct() {}; +}; + +struct Copy { + Copy() {}; + Copy(const Copy &) {}; +}; + +struct Move { + Move() {}; + Move(Move &&) {}; +}; + +struct VirtualFunc { + virtual void f() {}; +}; + +struct VirtualBase : virtual Base { + int vb; +}; + +template +void memset_temp(T *b) { + memset(b, 0, sizeof(T)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] +} + +template +void memcpy_temp(S *a, T *b) { + memcpy(a, b, sizeof(T)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] +} + +template +void memmove_temp(S *a, T *b) { + memmove(a, b, sizeof(T)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] +} + +void notTriviallyCopyable() { + Plain p; // TriviallyCopyable for variety + Destruct d; + Copy c; + Move m; + VirtualFunc vf; + VirtualBase vb; + + memset(&vf, 0, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + memset(&d, 0, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + memset(&c, 0, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + std::memset(&m, 0, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + ::memset(&vb, 0, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + + memcpy(&p, &vf, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] + memcpy(&p, &d, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] + memcpy(&c, &p, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + std::memcpy(&m, &p, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + ::memcpy(&vb, &p, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + + memmove(&vf, &p, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + memmove(&d, &p, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] + memmove(&p, &c, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] + std::memmove(&p, &m, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] + ::memmove(&p, &vb, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] + +#define MEMSET memset(&vf, 0, sizeof(int)); + MEMSET + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] +#define MEMCPY memcpy(&d, &p, sizeof(int)); + MEMCPY + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [misc-undefined-memory-manipulation] +#define MEMMOVE memmove(&p, &c, sizeof(int)); + MEMMOVE + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [misc-undefined-memory-manipulation] + + memset_temp(&vf); + memcpy_temp(&p, &vf); + memmove_temp(&p, &vf); +} + +void triviallyCopyable() { + Plain p; + Base base; + Derived derived; + + int i = 5; + int ia[3] = {1, 2, 3}; + float f = 3.14; + float fa[3] = {1.1, 2.2, 3.3}; + bool b = false; + bool ba[2] = {true, false}; + E e = X; + p.n = 2; + + memset(&p, 0, sizeof(int)); + memset(&base, 0, sizeof(float)); + memset(&derived, 0, sizeof(bool)); + memset(&i, 0, sizeof(int)); + memset(ia, 0, sizeof(int)); + memset(&f, 0, sizeof(float)); + memset(fa, 0, sizeof(float)); + memset(&b, 0, sizeof(bool)); + memset(ba, 0, sizeof(bool)); + memset(&e, 0, sizeof(int)); + memset(&p.n, 0, sizeof(int)); + + memcpy(&p, &p, sizeof(int)); + memcpy(&base, &base, sizeof(float)); + memcpy(&derived, &derived, sizeof(bool)); + memcpy(&i, &i, sizeof(int)); + memcpy(ia, ia, sizeof(int)); + memcpy(&f, &f, sizeof(float)); + memcpy(fa, fa, sizeof(float)); + memcpy(&b, &b, sizeof(bool)); + memcpy(ba, ba, sizeof(bool)); + memcpy(&e, &e, sizeof(int)); + memcpy(&p.n, &p.n, sizeof(int)); + + memmove(&p, &p, sizeof(int)); + memmove(&base, &base, sizeof(float)); + memmove(&derived, &derived, sizeof(bool)); + memmove(&i, &i, sizeof(int)); + memmove(ia, ia, sizeof(int)); + memmove(&f, &f, sizeof(float)); + memmove(fa, fa, sizeof(float)); + memmove(&b, &b, sizeof(bool)); + memmove(ba, ba, sizeof(bool)); + memmove(&e, &e, sizeof(int)); + memmove(&p.n, &p.n, sizeof(int)); +}