diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -8,6 +8,7 @@ MiscTidyModule.cpp MisplacedConstCheck.cpp NewDeleteOverloadsCheck.cpp + NoIntToPtrCheck.cpp NoRecursionCheck.cpp NonCopyableObjects.cpp NonPrivateMemberVariablesInClassesCheck.cpp diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -12,6 +12,7 @@ #include "DefinitionsInHeadersCheck.h" #include "MisplacedConstCheck.h" #include "NewDeleteOverloadsCheck.h" +#include "NoIntToPtrCheck.h" #include "NoRecursionCheck.h" #include "NonCopyableObjects.h" #include "NonPrivateMemberVariablesInClassesCheck.h" @@ -36,6 +37,7 @@ CheckFactories.registerCheck("misc-misplaced-const"); CheckFactories.registerCheck( "misc-new-delete-overloads"); + CheckFactories.registerCheck("misc-no-inttoptr"); CheckFactories.registerCheck("misc-no-recursion"); CheckFactories.registerCheck( "misc-non-copyable-objects"); diff --git a/clang-tools-extra/clang-tidy/misc/NoIntToPtrCheck.h b/clang-tools-extra/clang-tidy/misc/NoIntToPtrCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/NoIntToPtrCheck.h @@ -0,0 +1,34 @@ +//===--- NoIntToPtrCheck.h - clang-tidy -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOINTTOPTRCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOINTTOPTRCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// Diagnoses every integer to pointer cast. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-no-inttoptr.html +class NoIntToPtrCheck : public ClangTidyCheck { +public: + NoIntToPtrCheck(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_NOINTTOPTRCHECK_H diff --git a/clang-tools-extra/clang-tidy/misc/NoIntToPtrCheck.cpp b/clang-tools-extra/clang-tidy/misc/NoIntToPtrCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/NoIntToPtrCheck.cpp @@ -0,0 +1,31 @@ +//===--- NoIntToPtrCheck.cpp - clang-tidy ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NoIntToPtrCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +void NoIntToPtrCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(castExpr(hasCastKind(CK_IntegralToPointer)).bind("x"), + this); +} + +void NoIntToPtrCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedCast = Result.Nodes.getNodeAs("x"); + diag(MatchedCast->getBeginLoc(), "avoid integer to pointer casts"); +} + +} // namespace misc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -118,6 +118,11 @@ Alias to the :doc:`bugprone-signal-handler ` check. +- New :doc:`misc-no-inttoptr + ` check. + + Diagnoses every integer to pointer cast. + - New :doc:`readability-function-cognitive-complexity ` check. @@ -139,8 +144,8 @@ Now renames overridden virtual methods if the method they override has a style violation. - - Added support for specifying the style of scoped ``enum`` constants. If + + Added support for specifying the style of scoped ``enum`` constants. If unspecified, will fall back to the style for regular ``enum`` constants. - Removed `google-runtime-references` check because the rule it checks does diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -201,6 +201,7 @@ `misc-definitions-in-headers `_, "Yes" `misc-misplaced-const `_, `misc-new-delete-overloads `_, + `misc-no-inttoptr `_, `misc-no-recursion `_, `misc-non-copyable-objects `_, `misc-non-private-member-variables-in-classes `_, diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc-no-inttoptr.rst b/clang-tools-extra/docs/clang-tidy/checks/misc-no-inttoptr.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/misc-no-inttoptr.rst @@ -0,0 +1,45 @@ +.. title:: clang-tidy - misc-no-inttoptr + +misc-no-inttoptr +================ + +Diagnoses every integer to pointer cast. + +While casting an (integral) pointer to an integer is obvious - you just get +the integral value of the pointer, casting an integer to an (integral) pointer +is deceivingly different. While you will get a pointer with that integral value, +if you got that integral value via a pointer-to-integer cast originally, +the new pointer will lack the provenance information from the original pointer. + +So while (integral) pointer to integer casts are effectively no-ops, +and are transparent to the optimizer, integer to (integral) pointer casts +are *NOT* transparent, and may conceal information from optimizer. + +While that may be the intention, it is not always so. For example, +let's take a look at a routine to align the pointer up to the multiple of 16: +The obvious, naive implementation for that is: + +.. code-block:: c++ + + char* src(char* maybe_underbiased_ptr) { + uintptr_t maybe_underbiased_intptr = (uintptr_t)maybe_underbiased_ptr; + uintptr_t aligned_biased_intptr = maybe_underbiased_intptr + 15; + uintptr_t aligned_intptr = aligned_biased_intptr & (~15); + return (char*)aligned_intptr; // warning: avoid integer to pointer casts [misc-no-inttoptr] + } + +The check will rightfully diagnose that cast. + +But when provenance concealment is not the goal of the code, but an accident, +this example can be rewritten as follows, without using integer to pointer cast: + +.. code-block:: c++ + + char* + tgt(char* maybe_underbiased_ptr) { + uintptr_t maybe_underbiased_intptr = (uintptr_t)maybe_underbiased_ptr; + uintptr_t aligned_biased_intptr = maybe_underbiased_intptr + 15; + uintptr_t aligned_intptr = aligned_biased_intptr & (~15); + uintptr_t bias = aligned_intptr - maybe_underbiased_intptr; + return maybe_underbiased_ptr + bias; + } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc-no-inttoptr.c b/clang-tools-extra/test/clang-tidy/checkers/misc-no-inttoptr.c new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc-no-inttoptr.c @@ -0,0 +1,66 @@ +// RUN: %check_clang_tidy %s misc-no-inttoptr %t + +void *t0(char x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t1(signed char x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t2(unsigned char x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} + +void *t3(short x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t4(unsigned short x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t5(signed short x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} + +void *t6(int x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t7(unsigned int x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t8(signed int x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} + +void *t9(long x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t10(unsigned long x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t11(signed long x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} + +void *t12(long long x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t13(unsigned long long x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} +void *t14(signed long long x) { + return x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc-no-inttoptr.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc-no-inttoptr.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc-no-inttoptr.cpp @@ -0,0 +1,16 @@ +// RUN: %check_clang_tidy %s misc-no-inttoptr %t + +// can not implicitly cast int to a pointer. +// can not use static_cast<>() to cast integer to a pointer. +// can not use dynamic_cast<>() to cast integer to a pointer. +// can not use const_cast<>() to cast integer to a pointer. + +void *t0(long long int x) { + return (void *)x; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +} + +void *t1(int x) { + return reinterpret_cast(x); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid integer to pointer casts [misc-no-inttoptr] +}