Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyMiscModule + IncorrectPointerCastCheck.cpp MisplacedConstCheck.cpp UnconventionalAssignOperatorCheck.cpp DefinitionsInHeadersCheck.cpp Index: clang-tidy/misc/IncorrectPointerCastCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/IncorrectPointerCastCheck.h @@ -0,0 +1,50 @@ +//===--- IncorrectPointerCastCheck.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_INCORRECTPOINTERCASTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTPOINTERCASTCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// Warns for cases when pointer is cast and the pointed to type is incompatible +/// with allocated memory area type. This may lead to access memory based on +/// invalid memory layout. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-incorrect-pointer-cast.html +class IncorrectPointerCastCheck : public ClangTidyCheck { +public: + IncorrectPointerCastCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + WarnForDifferentSignedness( + Options.get("WarnForDifferentSignedness", false)), + IgnoreReinterpretCast(Options.get("IgnoreReinterpretCast", false)) {} + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + /// This option can be configured to warn when the pointed to type signedness + /// is different from the allocated type. The default is false because this + /// option might be noisy on some code bases. + const bool WarnForDifferentSignedness; + /// This option can be configured to do not warn when reinterpter cast is + /// used. The default is false. + const bool IgnoreReinterpretCast; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTPOINTERCASTCHECK_H Index: clang-tidy/misc/IncorrectPointerCastCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/IncorrectPointerCastCheck.cpp @@ -0,0 +1,113 @@ +//===--- IncorrectPointerCastCheck.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 "IncorrectPointerCastCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +void IncorrectPointerCastCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "WarnForDifferentSignedness", WarnForDifferentSignedness); + Options.store(Opts, "IgnoreReinterpretCast", IgnoreReinterpretCast); +} + +void IncorrectPointerCastCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(cStyleCastExpr(hasCastKind(CK_BitCast), + unless(isInTemplateInstantiation())) + .bind("cast"), + this); + if (!IgnoreReinterpretCast) { + Finder->addMatcher( + cxxReinterpretCastExpr(hasCastKind(CK_BitCast), + unless(isInTemplateInstantiation())) + .bind("cast"), + this); + } +} + +void IncorrectPointerCastCheck::check(const MatchFinder::MatchResult &Result) { + const ASTContext &Context = *Result.Context; + const auto *castExpr = Result.Nodes.getNodeAs("cast"); + + const QualType SrcType = castExpr->getSubExpr()->getType(); + const QualType DestType = castExpr->getType(); + + if (!SrcType->isPointerType() || !DestType->isPointerType()) + return; + + if (SrcType->isDependentType() || DestType->isDependentType()) + return; + + const QualType SrcPointedType = SrcType->getPointeeType(); + const QualType DestPointedType = DestType->getPointeeType(); + + if (SrcPointedType->isIncompleteType() || DestPointedType->isIncompleteType()) + return; + + if (Context.getIntWidth(SrcPointedType) < + Context.getIntWidth(DestPointedType)) { + diag(castExpr->getLocStart(), + "cast from %0 to %1 may lead to access memory based on invalid memory " + "layout; pointed to type is wider than the allocated type") + << SrcPointedType << DestPointedType; + } else if (Result.Context->getTypeAlign(SrcPointedType) < + Result.Context->getTypeAlign(DestPointedType)) { + diag(castExpr->getLocStart(), + "cast from %0 to %1 may lead to access memory based on invalid " + "memory layout; pointed to type is strictly aligned than the " + "allocated type") + << SrcPointedType << DestPointedType; + } else if (SrcPointedType->isStructureType() && + DestPointedType->isStructureType()) { + const auto *SrcTypeCXXRecordDecl = SrcPointedType->getAsCXXRecordDecl(); + const auto *DestTypeCXXRecordDecl = DestPointedType->getAsCXXRecordDecl(); + bool FieldsAreSame = true; + + for (RecordDecl::field_iterator + SrcIterator = SrcTypeCXXRecordDecl->field_begin(), + SrcEnd = SrcTypeCXXRecordDecl->field_end(), + DestIterator = DestTypeCXXRecordDecl->field_begin(), + DestEnd = DestTypeCXXRecordDecl->field_end(); + DestIterator != DestEnd && FieldsAreSame; + ++SrcIterator, ++DestIterator) { + const FieldDecl &SrcField = **SrcIterator; + const FieldDecl &DestField = **DestIterator; + if (SrcField.getType() != DestField.getType() || SrcIterator == SrcEnd) { + FieldsAreSame = false; + } + } + + if (!FieldsAreSame) { + diag(castExpr->getLocStart(), + "cast from %0 to %1 may lead to access memory based on invalid " + "memory layout; struct members are incompatible") + << SrcPointedType << DestPointedType; + } + } else if (WarnForDifferentSignedness && + ((SrcPointedType->isSignedIntegerType() && + DestPointedType->isUnsignedIntegerType()) || + (SrcPointedType->isUnsignedIntegerType() && + DestPointedType->isSignedIntegerType()))) { + diag(castExpr->getLocStart(), + "cast from %0 to %1 may lead to access memory based on invalid " + "memory layout; different signedness types") + << SrcPointedType << DestPointedType; + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "DefinitionsInHeadersCheck.h" +#include "IncorrectPointerCastCheck.h" #include "MisplacedConstCheck.h" #include "NewDeleteOverloadsCheck.h" #include "NonCopyableObjects.h" @@ -30,6 +31,8 @@ class MiscModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "misc-incorrect-pointer-cast"); CheckFactories.registerCheck("misc-misplaced-const"); CheckFactories.registerCheck( "misc-unconventional-assign-operator"); Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -133,6 +133,13 @@ Checks on ``switch`` and ``if`` - ``else if`` constructs that do not cover all possible code paths. +- New :doc:`misc-incorrect-pointer-cast + ` check + + Warns for cases when pointer is cast and the pointed to type is + incompatible with allocated memory area type. This may lead to access memory + based on invalid memory layout. + - New :doc:`modernize-use-uncaught-exceptions ` check. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -153,6 +153,7 @@ llvm-namespace-comment llvm-twine-local misc-definitions-in-headers + misc-incorrect-pointer-cast misc-misplaced-const misc-new-delete-overloads misc-non-copyable-objects Index: docs/clang-tidy/checks/misc-incorrect-pointer-cast.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-incorrect-pointer-cast.rst @@ -0,0 +1,85 @@ +.. title:: clang-tidy - misc-incorrect-pointer-cast + +misc-incorrect-pointer-cast +=========================== + +Warns for cases when pointer is cast and the pointed to type is wider than the +allocated type. +For example char vs integer, long vs char etc. +Also warns for cases when the pointed to type layout is different from the +allocated type layout, like different structs, integer vs float/double, +different signedness. + +Allows pointer casts if the pointed to struct type is "part" of the allocated +type. +Which means the allocated type contains the pointed to type member by member. + +Options +------- + +.. option:: WarnForDifferentSignedness + + This option can be configured to warn when the pointed to type signedness + is different from the allocated type. + Disabled by default because this option might be noisy on some code bases. + +.. option:: IgnoreReinterpretCast + + This option can be configured to do not warn when reinterpter cast is used. + Disabled by default but this option might be useful on code bases where + `reinterpret_cast` is used carefully. + +Examles +------- + +Cast char pointer to integer pointer. +Check will warn because of cast to a wider type. + +.. code-block:: c++ + + char c = 'a'; + int *i = (int *)&c; + +Cast between structs. +Check will allow to cast to a narrower struct if it is part of the source struct +member by member. + +.. code-block:: c++ + + struct S1 { + int a; + }; + + struct S2 { + int a; + double b; + }; + + struct S3 { + double y; + long x; + }; + + struct S2 s2; + struct S1 *s1 = (struct S1 *)&s2; // Won't warn. Struct "S2" contains struct + // "S2" member by member. + struct S3 *s3 = (struct S3 *)&s2; // Warning because of different type + // layout. + +Cast with `reinterpret_cast`. +If the `IgnoreReinterpretCast` option is `0`, check will warn for these +kind of casts. + +.. code-block:: c++ + + char c = 'x'; + int *i = reinterpret_cast(&c); + +Cast between different signedness types. +If the `WarnForDifferentSignedness` option is `1`, check will warn for these +kind of casts. + +.. code-block:: c++ + + unsigned int u; + int i = (int *)&u; Index: test/clang-tidy/misc-incorrect-pointer-cast.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-incorrect-pointer-cast.cpp @@ -0,0 +1,201 @@ +// RUN: %check_clang_tidy %s misc-incorrect-pointer-cast %t -- \ +// RUN: -config="{CheckOptions: [{key: misc-incorrect-pointer-cast.WarnForDifferentSignedness, value: 1}]}" -- + +char __attribute__((aligned(4))) a[16]; + +struct S0 { + char a[16]; +}; + +struct S01 { + char __attribute__((aligned(4))) a[16]; + struct S0 __attribute__((aligned(4))) s0; +}; + +struct S1 { + int a; + int b; +}; + +struct S2 { + int a; +}; + +struct S3 { + int a; + double b; +}; + +struct S4 { + int x; + double y; +}; + +struct S5 { + double y; + int x; +}; + +struct __attribute__((aligned(16))) SAligned { + char buffer[16]; +}; + +void initDouble(double *d) { + *d = 0.5; +} + +void castCharToInt(void) { + char c = 'x'; + int *i = (int *)&c; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: cast from 'char' to 'int' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void castCharToSort() { + char c; + short *i = (short *)&c; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: cast from 'char' to 'short' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void castShortToInt() { + short s; + int *i = (int *)&s; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: cast from 'short' to 'int' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void castWideCharToLong() { + wchar_t wc; + long *f = (long *)&wc; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: cast from 'wchar_t' to 'long' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void castFloatToDouble() { + float f; + initDouble((double *)&f); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: cast from 'float' to 'double' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void castToS2(char *data, unsigned offset) { + struct S2 *tmp; + struct S2 header; + + tmp = (struct S2 *)(data + offset); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: cast from 'char' to 'struct S2' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void castS3ToS1() { + struct S3 s3; + struct S1 *s1 = (struct S1 *)&s3; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: cast from 'struct S3' to 'struct S1' may lead to access memory based on invalid memory layout; struct members are incompatible [misc-incorrect-pointer-cast] +} + +void castS4ToS5() { + struct S4 s4; + struct S5 *s5 = (struct S5 *)&s4; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: cast from 'struct S4' to 'struct S5' may lead to access memory based on invalid memory layout; struct members are incompatible [misc-incorrect-pointer-cast] +} + +void castULongToLong() { + unsigned long ul; + long *l = (long *)&ul; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: cast from 'unsigned long' to 'long' may lead to access memory based on invalid memory layout; different signedness types [misc-incorrect-pointer-cast] +} + +void castIntToUInt() { + int i; + unsigned int *ui = (unsigned int *)&i; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: cast from 'int' to 'unsigned int' may lead to access memory based on invalid memory layout; different signedness types [misc-incorrect-pointer-cast] +} + +void castToAlignedStruct(char *P) { + struct SAligned *a = (struct SAligned *)P; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: cast from 'char' to 'struct SAligned' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void castCharToIntWithReinterpretCast(void) { + char c = 'x'; + int *i = reinterpret_cast(&c); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: cast from 'char' to 'int' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +void TestDifferentAlignment() { + + struct S01 s; + int *i = (int *)s.a; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: cast from 'char' to 'int' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] + i = (int *)&s.s0; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: cast from 'struct S0' to 'int' may lead to access memory based on invalid memory layout; pointed to type is strictly aligned than the allocated type [misc-incorrect-pointer-cast] + i = (int *)a; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: cast from 'char' to 'int' may lead to access memory based on invalid memory layout; pointed to type is wider than the allocated type [misc-incorrect-pointer-cast] +} + +// negatives +void castIntToFloat() { + int i; + float *f = (float *)&i; +} + +void castCharToChar(char *p) { + char *c = (char *)p; +} + +void castShortToChar() { + short s; + char *c = (char *)&s; +} + +void initInt(int *i) { + *i = 1; +} + +void castIntToInt() { + int i; + initInt(&i); +} + +void castS1ToS2() { + struct S1 s1; + struct S2 *s2 = (struct S2 *)&s1; +} + +void castS4ToS3() { + struct S4 s4; + struct S3 *s3 = (struct S3 *)&s4; +} + +void IncompleteType(char *P) { + struct B *b = (struct B *)P; +} + +// Casts from void* are a special case. +void CastFromVoidPointer(void *P) { + char *a = (char *)P; + short *b = (short *)P; + int *c = (int *)P; + + const volatile void *P2 = P; + char *d = (char *)P2; + short *e = (short *)P2; + int *f = (int *)P2; + + const char *g = (const char *)P2; + const short *h = (const short *)P2; + const int *i = (const int *)P2; + + const volatile char *j = (const volatile char *)P2; + const volatile short *k = (const volatile short *)P2; + const volatile int *l = (const volatile int *)P2; +} + +typedef int (*FnTy)(void); +unsigned int func(void); + +FnTy testFunc(void) { + return (FnTy)&func; +} + +struct W; + +void function3(struct W *v) { + int *i = (int *)v; + struct W *u = (struct W *)i; +}