https://github.com/balazske updated https://github.com/llvm/llvm-project/pull/153428
From 95634ba1fa9690f3d95c45005c908e642c1ced79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.k...@ericsson.com> Date: Wed, 13 Aug 2025 15:13:13 +0200 Subject: [PATCH 1/2] [clang-tidy] Add check 'bugprone-cast-to-struct' --- .../bugprone/BugproneTidyModule.cpp | 2 + .../clang-tidy/bugprone/CMakeLists.txt | 1 + .../clang-tidy/bugprone/CastToStructCheck.cpp | 82 +++++++++++++++++++ .../clang-tidy/bugprone/CastToStructCheck.h | 39 +++++++++ clang-tools-extra/docs/ReleaseNotes.rst | 5 ++ .../checks/bugprone/cast-to-struct.rst | 54 ++++++++++++ .../docs/clang-tidy/checks/list.rst | 1 + .../checkers/bugprone/cast-to-struct-ignore.c | 40 +++++++++ .../checkers/bugprone/cast-to-struct.c | 66 +++++++++++++++ 9 files changed, 290 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 824ebdfbd00dc..5a1573eb34ee3 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -18,6 +18,7 @@ #include "BranchCloneCheck.h" #include "CapturingThisInMemberVariableCheck.h" #include "CastingThroughVoidCheck.h" +#include "CastToStructCheck.h" #include "ChainedComparisonCheck.h" #include "ComparePointerToMemberVirtualFunctionCheck.h" #include "CopyConstructorInitCheck.h" @@ -125,6 +126,7 @@ class BugproneModule : public ClangTidyModule { "bugprone-capturing-this-in-member-variable"); CheckFactories.registerCheck<CastingThroughVoidCheck>( "bugprone-casting-through-void"); + CheckFactories.registerCheck<CastToStructCheck>("bugprone-cast-to-struct"); CheckFactories.registerCheck<ChainedComparisonCheck>( "bugprone-chained-comparison"); CheckFactories.registerCheck<ComparePointerToMemberVirtualFunctionCheck>( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 59928e5e47a09..c2bd0d1e3456c 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -14,6 +14,7 @@ add_clang_library(clangTidyBugproneModule STATIC BugproneTidyModule.cpp CapturingThisInMemberVariableCheck.cpp CastingThroughVoidCheck.cpp + CastToStructCheck.cpp ChainedComparisonCheck.cpp ComparePointerToMemberVirtualFunctionCheck.cpp CopyConstructorInitCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp new file mode 100644 index 0000000000000..374736d48d1c8 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp @@ -0,0 +1,82 @@ +//===--- CastToStructCheck.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 "CastToStructCheck.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +CastToStructCheck::CastToStructCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IgnoredFunctions( + utils::options::parseStringList(Options.get("IgnoredFunctions", ""))), + IgnoredFromTypes( + utils::options::parseStringList(Options.get("IgnoredFromTypes", ""))), + IgnoredToTypes( + utils::options::parseStringList(Options.get("IgnoredToTypes", ""))) {} + +void CastToStructCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoredFunctions", + utils::options::serializeStringList(IgnoredFunctions)); + Options.store(Opts, "IgnoredFromTypes", + utils::options::serializeStringList(IgnoredFromTypes)); + Options.store(Opts, "IgnoredToTypes", + utils::options::serializeStringList(IgnoredToTypes)); +} + +void CastToStructCheck::registerMatchers(MatchFinder *Finder) { + auto FromPointee = + qualType(hasUnqualifiedDesugaredType(type().bind("FromType")), + unless(qualType(matchers::matchesAnyListedTypeName( + IgnoredFromTypes, false)))) + .bind("FromPointee"); + auto ToPointee = + qualType(hasUnqualifiedDesugaredType(recordType().bind("ToType")), + unless(qualType( + matchers::matchesAnyListedTypeName(IgnoredToTypes, false)))) + .bind("ToPointee"); + auto FromPtrType = qualType(pointsTo(FromPointee)).bind("FromPtr"); + auto ToPtrType = qualType(pointsTo(ToPointee)).bind("ToPtr"); + Finder->addMatcher( + cStyleCastExpr(hasSourceExpression(hasType(FromPtrType)), + hasType(ToPtrType), + unless(hasAncestor(functionDecl( + matchers::matchesAnyListedName(IgnoredFunctions))))) + .bind("CastExpr"), + this); +} + +void CastToStructCheck::check(const MatchFinder::MatchResult &Result) { + const auto *const FoundCastExpr = + Result.Nodes.getNodeAs<CStyleCastExpr>("CastExpr"); + const auto *const FromPtr = Result.Nodes.getNodeAs<QualType>("FromPtr"); + const auto *const ToPtr = Result.Nodes.getNodeAs<QualType>("ToPtr"); + const auto *const FromPointee = + Result.Nodes.getNodeAs<QualType>("FromPointee"); + const auto *const ToPointee = Result.Nodes.getNodeAs<QualType>("ToPointee"); + const auto *const FromType = Result.Nodes.getNodeAs<Type>("FromType"); + const auto *const ToType = Result.Nodes.getNodeAs<RecordType>("ToType"); + if (!FromPointee || !ToPointee) + return; + if (FromType->isVoidType() || FromType->isUnionType() || + ToType->isUnionType()) + return; + if (FromType == ToType) + return; + diag(FoundCastExpr->getExprLoc(), + "casting a %0 pointer to a " + "%1 pointer and accessing a field can lead to memory " + "access errors or data corruption") + << *FromPtr << *ToPtr; +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h new file mode 100644 index 0000000000000..9e6c868824d23 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h @@ -0,0 +1,39 @@ +//===--- CastToStructCheck.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_BUGPRONE_CASTTOSTRUCTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTTOSTRUCTCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// Finds casts from pointers to struct or scalar type to pointers to struct +/// type. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/cast-to-struct.html +class CastToStructCheck : public ClangTidyCheck { +public: + CastToStructCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.C99; + } + +private: + std::vector<llvm::StringRef> IgnoredFunctions; + std::vector<llvm::StringRef> IgnoredFromTypes; + std::vector<llvm::StringRef> IgnoredToTypes; +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTTOSTRUCTCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 187aae2ec8c90..a13072123b9ef 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -112,6 +112,11 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`bugprone-cast-to-struct + <clang-tidy/checks/bugprone/cast-to-struct>` check. + + Finds casts from pointers to struct or scalar type to pointers to struct type. + - New :doc:`bugprone-invalid-enum-default-initialization <clang-tidy/checks/bugprone/invalid-enum-default-initialization>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst new file mode 100644 index 0000000000000..79864935d3ea1 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst @@ -0,0 +1,54 @@ +.. title:: clang-tidy - bugprone-cast-to-struct + +bugprone-cast-to-struct +======================= + +Finds casts from pointers to struct or scalar type to pointers to struct type. + +Casts between pointers to different structs can be unsafe because it is possible +to access uninitialized or undefined data after the cast. There may be issues +with type compatibility or data alignment. Cast from a pointer to a scalar type +(which points often to an array or memory block) to a `struct` type pointer can +be unsafe for similar reasons. This check warns at casts from any non-`struct` +type to a `struct` type. No warning is produced at cast from type `void *` (this +is the usual way of allocating memory with `malloc`-like functions). It is +possible to specify additional types to ignore by the check. In addition, +`union` types are completely excluded from the check. The check does not take +into account type compatibility or data layout, only the names of the types. + +.. code-block:: c + + void test1(char *p) { + struct S1 *s; + s = (struct S1 *)p; // warn: 'char *' is converted to 'struct S1 *' + } + + void test2(struct S1 *p) { + struct S2 *s; + s = (struct S2 *)p; // warn: 'struct S1 *' is converted to 'struct S2 *' + } + + void test3(void) { + struct S1 *s; + s = (struct S1 *)calloc(1, sizeof(struct S1)); // no warning + } + +Options +------- + +.. option:: IgnoredFromTypes + + Semicolon-separated list of types for which the checker should not warn if + encountered at cast source. Can contain regular expressions. The `*` + character (for pointer type) is not needed in the type names. + +.. option:: IgnoredToTypes + + Semicolon-separated list of types for which the checker should not warn if + encountered at cast destination. Can contain regular expressions. The `*` + character (for pointer type) is not needed in the type names. + +.. option:: IgnoredFunctions + + List of function names from which the checker should produce no warnings. Can + contain regular expressions. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index b6444eb3c9aec..5f8ae4a6eabb7 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -85,6 +85,7 @@ Clang-Tidy Checks :doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes" :doc:`bugprone-branch-clone <bugprone/branch-clone>`, :doc:`bugprone-capturing-this-in-member-variable <bugprone/capturing-this-in-member-variable>`, + :doc:`bugprone-cast-to-struct <bugprone/cast-to-struct>`, :doc:`bugprone-casting-through-void <bugprone/casting-through-void>`, :doc:`bugprone-chained-comparison <bugprone/chained-comparison>`, :doc:`bugprone-compare-pointer-to-member-virtual-function <bugprone/compare-pointer-to-member-virtual-function>`, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c new file mode 100644 index 0000000000000..1cc4744220f4a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c @@ -0,0 +1,40 @@ +// RUN: %check_clang_tidy -check-suffixes=FUNC %s bugprone-cast-to-struct %t -- \ +// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFunctions: 'ignored_f$'}}" +// RUN: %check_clang_tidy -check-suffixes=FROM-TY %s bugprone-cast-to-struct %t -- \ +// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFromTypes: 'int'}}" +// RUN: %check_clang_tidy -check-suffixes=TO-TY %s bugprone-cast-to-struct %t -- \ +// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredToTypes: 'IgnoredType'}}" + +struct IgnoredType { + int a; +}; + +struct OtherType { + int a; + int b; +}; + +void ignored_f(char *p) { + struct OtherType *p1; + p1 = (struct OtherType *)p; + // CHECK-MESSAGES-FROM-TY: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption + // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption +} + +void ignored_from_type(int *p) { + struct OtherType *p1; + p1 = (struct OtherType *)p; + // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption + // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption +} + +void ignored_to_type(char *p) { + struct IgnoredType *p1; + p1 = (struct IgnoredType *)p; + // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption + // CHECK-MESSAGES-FROM-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption +} + +struct OtherType *test_void_is_always_ignored(void *p) { + return (struct OtherType *)p; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c new file mode 100644 index 0000000000000..cfabf69351f39 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c @@ -0,0 +1,66 @@ +// RUN: %check_clang_tidy %s bugprone-cast-to-struct %t + +struct S1 { + int a; +}; + +struct S2 { + char a; +}; + +union U1 { + int a; + char b; +}; + +union U2 { + struct S1 a; + char b; +}; + +typedef struct S1 TyS1; +typedef struct S1 *TyPS1; + +typedef union U1 *TyPU1; + +typedef int int_t; +typedef int * int_ptr_t; + +struct S1 *test_simple(char *p) { + return (struct S1 *)p; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: casting a 'char *' pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct] + struct S1 *s; + int i; + s = (struct S1 *)&i; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: casting a 'int *' pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct] +} + +struct S1 *test_cast_from_void(void *p) { + return (struct S1 *)p; +} + +struct S1 *test_cast_from_struct(struct S2 *p) { + return (struct S1 *)p; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: casting a 'struct S2 *' pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct] +} + +TyPS1 test_cast_from_similar(struct S1 *p) { + return (TyPS1)p; +} + +void test_typedef(char *p1, int_t *p2, int_ptr_t p3) { + TyS1 *a = (TyS1 *)p1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: casting a 'char *' pointer to a 'TyS1 *' (aka 'struct S1 *') pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct] + TyPS1 b = (TyPS1)p1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: casting a 'char *' pointer to a 'TyPS1' (aka 'struct S1 *') pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct] + struct S1 *c = (struct S1 *)p2; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: casting a 'int_t *' (aka 'int *') pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct] + struct S1 *d = (struct S1 *)p3; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: casting a 'int_ptr_t' (aka 'int *') pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct] +} + +void test_union(char *p1, union U1 *p2, TyPU1 p3) { + union U1 *a = (union U1 *)p1; + struct S1 *b = (struct S1 *)p2; + struct S1 *c = (struct S1 *)p3; +} From f2eb00df8326548801b019450ed36f3ff4d476c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.k...@ericsson.com> Date: Fri, 15 Aug 2025 16:21:23 +0200 Subject: [PATCH 2/2] updated documentation, changed options --- .../clang-tidy/bugprone/CastToStructCheck.cpp | 58 +++++++++---------- .../clang-tidy/bugprone/CastToStructCheck.h | 4 +- .../checks/bugprone/cast-to-struct.rst | 54 +++++++++-------- .../checkers/bugprone/cast-to-struct-ignore.c | 51 +++++++--------- 4 files changed, 82 insertions(+), 85 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp index 374736d48d1c8..9915d32e1ddbd 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp @@ -17,42 +17,31 @@ namespace clang::tidy::bugprone { CastToStructCheck::CastToStructCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), - IgnoredFunctions( - utils::options::parseStringList(Options.get("IgnoredFunctions", ""))), - IgnoredFromTypes( - utils::options::parseStringList(Options.get("IgnoredFromTypes", ""))), - IgnoredToTypes( - utils::options::parseStringList(Options.get("IgnoredToTypes", ""))) {} + IgnoredCasts( + utils::options::parseStringList(Options.get("IgnoredCasts", ""))) {} void CastToStructCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "IgnoredFunctions", - utils::options::serializeStringList(IgnoredFunctions)); - Options.store(Opts, "IgnoredFromTypes", - utils::options::serializeStringList(IgnoredFromTypes)); - Options.store(Opts, "IgnoredToTypes", - utils::options::serializeStringList(IgnoredToTypes)); + Options.store(Opts, "IgnoredCasts", + utils::options::serializeStringList(IgnoredCasts)); } void CastToStructCheck::registerMatchers(MatchFinder *Finder) { auto FromPointee = qualType(hasUnqualifiedDesugaredType(type().bind("FromType")), - unless(qualType(matchers::matchesAnyListedTypeName( - IgnoredFromTypes, false)))) + unless(voidType()), + unless(hasDeclaration(recordDecl(isUnion())))) .bind("FromPointee"); auto ToPointee = - qualType(hasUnqualifiedDesugaredType(recordType().bind("ToType")), - unless(qualType( - matchers::matchesAnyListedTypeName(IgnoredToTypes, false)))) + qualType(hasUnqualifiedDesugaredType( + recordType(unless(hasDeclaration(recordDecl(isUnion())))) + .bind("ToType"))) .bind("ToPointee"); auto FromPtrType = qualType(pointsTo(FromPointee)).bind("FromPtr"); auto ToPtrType = qualType(pointsTo(ToPointee)).bind("ToPtr"); - Finder->addMatcher( - cStyleCastExpr(hasSourceExpression(hasType(FromPtrType)), - hasType(ToPtrType), - unless(hasAncestor(functionDecl( - matchers::matchesAnyListedName(IgnoredFunctions))))) - .bind("CastExpr"), - this); + Finder->addMatcher(cStyleCastExpr(hasSourceExpression(hasType(FromPtrType)), + hasType(ToPtrType)) + .bind("CastExpr"), + this); } void CastToStructCheck::check(const MatchFinder::MatchResult &Result) { @@ -65,13 +54,24 @@ void CastToStructCheck::check(const MatchFinder::MatchResult &Result) { const auto *const ToPointee = Result.Nodes.getNodeAs<QualType>("ToPointee"); const auto *const FromType = Result.Nodes.getNodeAs<Type>("FromType"); const auto *const ToType = Result.Nodes.getNodeAs<RecordType>("ToType"); - if (!FromPointee || !ToPointee) - return; - if (FromType->isVoidType() || FromType->isUnionType() || - ToType->isUnionType()) - return; + if (FromType == ToType) return; + + const std::string FromName = FromPointee->getAsString(); + const std::string ToName = ToPointee->getAsString(); + llvm::Regex FromR; + llvm::Regex ToR; + for (auto [Idx, Str] : llvm::enumerate(IgnoredCasts)) { + if (Idx % 2 == 0) { + FromR = llvm::Regex(Str); + } else { + ToR = llvm::Regex(Str); + if (FromR.match(FromName) && ToR.match(ToName)) + return; + } + } + diag(FoundCastExpr->getExprLoc(), "casting a %0 pointer to a " "%1 pointer and accessing a field can lead to memory " diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h index 9e6c868824d23..ec89fa8ab6dfc 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h @@ -29,9 +29,7 @@ class CastToStructCheck : public ClangTidyCheck { } private: - std::vector<llvm::StringRef> IgnoredFunctions; - std::vector<llvm::StringRef> IgnoredFromTypes; - std::vector<llvm::StringRef> IgnoredToTypes; + std::vector<llvm::StringRef> IgnoredCasts; }; } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst index 79864935d3ea1..9b9afa43b6a96 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst @@ -6,15 +6,15 @@ bugprone-cast-to-struct Finds casts from pointers to struct or scalar type to pointers to struct type. Casts between pointers to different structs can be unsafe because it is possible -to access uninitialized or undefined data after the cast. There may be issues -with type compatibility or data alignment. Cast from a pointer to a scalar type -(which points often to an array or memory block) to a `struct` type pointer can -be unsafe for similar reasons. This check warns at casts from any non-`struct` -type to a `struct` type. No warning is produced at cast from type `void *` (this -is the usual way of allocating memory with `malloc`-like functions). It is -possible to specify additional types to ignore by the check. In addition, -`union` types are completely excluded from the check. The check does not take -into account type compatibility or data layout, only the names of the types. +to access uninitialized or undefined data after the cast. Cast from a +scalar-type pointer (which points often to an array or memory block) to a +``struct`` type pointer can be unsafe for similar reasons. This check warns at +pointer casts from any non-struct type to a struct type. No warning is produced +at cast from type ``void *`` (this is the usual way of allocating memory with +``malloc``-like functions). In addition, ``union`` types are excluded from the +check. It is possible to specify additional types to ignore. The check does not +take into account type compatibility or data layout, only the names of the +types. .. code-block:: c @@ -33,22 +33,28 @@ into account type compatibility or data layout, only the names of the types. s = (struct S1 *)calloc(1, sizeof(struct S1)); // no warning } -Options -------- - -.. option:: IgnoredFromTypes +Limitations +----------- - Semicolon-separated list of types for which the checker should not warn if - encountered at cast source. Can contain regular expressions. The `*` - character (for pointer type) is not needed in the type names. +The check does run only on `C` code. -.. option:: IgnoredToTypes +C-style casts are discouraged in `C++` and should be converted to more type-safe +casts. The ``reinterpreted_cast`` is used for the most unsafe cases and +indicates by itself a potentially dangerous operation. Additionally, inheritance +and dynamic types would make such a check less useful. - Semicolon-separated list of types for which the checker should not warn if - encountered at cast destination. Can contain regular expressions. The `*` - character (for pointer type) is not needed in the type names. - -.. option:: IgnoredFunctions +Options +------- - List of function names from which the checker should produce no warnings. Can - contain regular expressions. +.. option:: IgnoredCasts + + Can contain a semicolon-separated list of type names that specify cast + types to ignore. The list should contain pairs of type names in a way that + the first type is the "from" type, the second is the "to" type in a cast + expression. The types in a pair and the pairs itself are separated by + ``;`` characters. For example ``char;Type1;char;Type2`` specifies that the + check does not produce warning for casts from ``char *`` to ``Type1 *`` and + casts from ``char *`` to ``Type2 *``. The list entries can be regular + expressions. The type name in the cast expression is matched without + resolution of type aliases like ``typedef``. Default value is empty list. + (Casts from ``void *`` are ignored always regardless of this list.) diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c index 1cc4744220f4a..e64fe177448d9 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c @@ -1,40 +1,33 @@ -// RUN: %check_clang_tidy -check-suffixes=FUNC %s bugprone-cast-to-struct %t -- \ -// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFunctions: 'ignored_f$'}}" -// RUN: %check_clang_tidy -check-suffixes=FROM-TY %s bugprone-cast-to-struct %t -- \ -// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFromTypes: 'int'}}" -// RUN: %check_clang_tidy -check-suffixes=TO-TY %s bugprone-cast-to-struct %t -- \ -// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredToTypes: 'IgnoredType'}}" +// RUN: %check_clang_tidy %s bugprone-cast-to-struct %t -- \ +// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: 'char;S1;int;Other*'}}" -struct IgnoredType { +struct S1 { int a; }; -struct OtherType { +struct S2 { + char a; +}; + +struct OtherS { int a; int b; }; -void ignored_f(char *p) { - struct OtherType *p1; - p1 = (struct OtherType *)p; - // CHECK-MESSAGES-FROM-TY: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption - // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption -} - -void ignored_from_type(int *p) { - struct OtherType *p1; - p1 = (struct OtherType *)p; - // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption - // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption -} - -void ignored_to_type(char *p) { - struct IgnoredType *p1; - p1 = (struct IgnoredType *)p; - // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption - // CHECK-MESSAGES-FROM-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption +void test1(char *p1, int *p2) { + struct S1 *s1; + s1 = (struct S1 *)p1; + struct S2 *s2; + s2 = (struct S2 *)p1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct S2 *' + s2 = (struct S2 *)p2; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'int *' pointer to a 'struct S2 *' + struct OtherS *s3; + s3 = (struct OtherS *)p2; + s3 = (struct OtherS *)p1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct OtherS *' } -struct OtherType *test_void_is_always_ignored(void *p) { - return (struct OtherType *)p; +struct S2 *test_void_is_always_ignored(void *p) { + return (struct S2 *)p; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits