f00kat updated this revision to Diff 250556.
f00kat added a comment.
Fix 'const auto*' in function getDescendantValueDecl
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D76229/new/
https://reviews.llvm.org/D76229
Files:
clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
clang-tools-extra/clang-tidy/cert/CMakeLists.txt
clang-tools-extra/clang-tidy/cert/PlacementNewStorageCheck.cpp
clang-tools-extra/clang-tidy/cert/PlacementNewStorageCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/cert-mem54-cpp.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/test/clang-tidy/checkers/cert-mem54-cpp.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/cert-mem54-cpp.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cert-mem54-cpp.cpp
@@ -0,0 +1,185 @@
+// RUN: %check_clang_tidy %s cert-mem54-cpp %t
+
+inline void *operator new(size_t s, void *p) noexcept {
+ (void)s;
+ return p;
+}
+
+inline void *operator new[](size_t s, void *p) noexcept {
+ (void)s;
+ return p;
+}
+
+void test1()
+{
+ // bad (short is aligned to 2).
+ short s1;
+ ::new (&s1) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 's1' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ // ok (same alignment because types are the same).
+ long s2;
+ ::new (&s2) long;
+
+ // ok (long long is greater then long).
+ long long s3;
+ ::new (&s3) long;
+
+ // bad (alias for type 'short' which is aligned to 2).
+ using s4t = short;
+ s4t s4;
+ ::new (&s4) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 's4' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ // ok (skip void pointer).
+ void* s5 = nullptr;
+ ::new (s5) long;
+
+ // bad (short is aligned to 2).
+ short* s6 = nullptr;
+ ::new (s6) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 's6' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ // bad (just check for pointer to pointer).
+ short **s7;
+ ::new (*s7) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 's7' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+}
+
+void test2()
+{
+ // ok.
+ alignas(long) unsigned char buffer1[sizeof(long)];
+ ::new (buffer1) long;
+
+ // bad (buffer is aligned to 'short' instead of 'long').
+ alignas(short) unsigned char buffer2[sizeof(long)];
+ ::new (buffer2) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'buffer2' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ // bad (not enough space).
+ alignas(long) unsigned char buffer3[sizeof(short)];
+ ::new (buffer3) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 2 bytes are not enough for allocation type 'long' which requires 4 bytes [cert-mem54-cpp]
+
+ // bad (still not enough space).
+ alignas(long) unsigned char buffer4[sizeof(short) + 1];
+ ::new (buffer4) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 3 bytes are not enough for allocation type 'long' which requires 4 bytes [cert-mem54-cpp]
+}
+
+void test3()
+{
+ // ok (100 % 4 == 0).
+ ::new ((size_t*)100) long;
+
+ // bad (100 % 4 == 2).
+ ::new ((size_t*)102) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: address is aligned to 2 bytes instead of 4 bytes [cert-mem54-cpp]
+
+ ::new (reinterpret_cast<size_t*>(100)) long;
+
+ ::new (reinterpret_cast<size_t*>(102)) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: address is aligned to 2 bytes instead of 4 bytes [cert-mem54-cpp]
+
+ ::new ((size_t*)(100 + 0)) long;
+
+ ::new ((size_t*)(100 + 2)) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: address is aligned to 2 bytes instead of 4 bytes [cert-mem54-cpp]
+}
+
+short* test4_f1()
+{
+ return nullptr;
+}
+
+short& test4_f2()
+{
+ static short v;
+ return v;
+}
+
+void test4()
+{
+ ::new (test4_f1()) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'test4_f1' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ ::new (&test4_f2()) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'test4_f2' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ short* (*p1)();
+ p1 = test4_f1;
+ ::new (p1()) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'p1' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+}
+
+void test5()
+{
+ struct S
+ {
+ short a;
+ };
+
+ // bad (not enough space).
+ const unsigned N = 32;
+ alignas(S) unsigned char buffer1[sizeof(S) * N];
+ ::new (buffer1) S[N];
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 64 bytes are not enough for array allocation which requires 64 bytes [cert-mem54-cpp]
+
+ // maybe ok but we need to warn.
+ alignas(S) unsigned char buffer2[sizeof(S) * N + sizeof(int)];
+ ::new (buffer2) S[N];
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: possibly not enough 68 bytes for array allocation which requires 64 bytes. current overhead requires the size of 4 bytes [cert-mem54-cpp]
+}
+
+void test6()
+{
+ struct A
+ {
+ char a;
+ char b;
+ } Ai;
+
+ // bad (struct A is aligned to char).
+ ::new (&Ai.a) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'Ai' is aligned to 1 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ struct B
+ {
+ char a;
+ long b;
+ } Bi;
+
+ // ok (struct B is aligned to long).
+ ::new (&Bi.a) long;
+
+ struct C
+ {
+ char a;
+ alignas(2) char b;
+ } Ci;
+
+ // bad (struct C is aligned to 2).
+ ::new (&Ci.a) long;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'Ci' is aligned to 2 bytes but allocated type 'long' is aligned to 4 bytes [cert-mem54-cpp]
+
+ struct D
+ {
+ char a;
+ char b;
+ struct {
+ long c;
+ };
+ } Di;
+
+ // ok (struct D is aligned to long).
+ ::new (&Di.a) long;
+
+ struct alignas(alignof(long)) E {
+ char a;
+ char b;
+ } Ei;
+
+ // ok (struct E is aligned to long).
+ ::new (&Ei.a) long;
+}
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -105,6 +105,7 @@
`cert-err58-cpp <cert-err58-cpp.html>`_,
`cert-err60-cpp <cert-err60-cpp.html>`_,
`cert-flp30-c <cert-flp30-c.html>`_,
+ `cert-mem54-cpp <cert-mem54-cpp.html>`_,
`cert-mem57-cpp <cert-mem57-cpp.html>`_,
`cert-msc50-cpp <cert-msc50-cpp.html>`_,
`cert-msc51-cpp <cert-msc51-cpp.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/cert-mem54-cpp.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/cert-mem54-cpp.rst
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-mem54-cpp
+
+cert-mem54-cpp
+==============
+
+Checks that placement ``new`` provided with properly aligned pointer to sufficient storage capacity.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`MEM54-CPP. Provide placement new with properly aligned pointers to sufficient storage capacity
+<https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM54-CPP.+Provide+placement+new+with+properly+aligned+pointers+to+sufficient+storage+capacity>`_.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -89,6 +89,12 @@
file, which often leads to hard-to-track-down ODR violations, and diagnoses
them.
+- New :doc:`cert-mem54-cpp
+ <clang-tidy/checks/cert-mem54-cpp>` check.
+
+ Checks that placement ``new`` provided with properly aligned pointer to
+ sufficient storage capacity.
+
- New :doc:`cert-oop57-cpp
<clang-tidy/checks/cert-oop57-cpp>` check.
Index: clang-tools-extra/clang-tidy/cert/PlacementNewStorageCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cert/PlacementNewStorageCheck.h
@@ -0,0 +1,35 @@
+//===--- PlacementNewStorageCheck.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_CERT_PLACEMENTNEWSTORAGECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PLACEMENTNEWSTORAGECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Checks that placement new provided with properly aligned pointer to
+/// sufficient storage capacity
+class PlacementNewStorageCheck : public ClangTidyCheck {
+public:
+ PlacementNewStorageCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PLACEMENTNEWSTORAGECHECK_H
Index: clang-tools-extra/clang-tidy/cert/PlacementNewStorageCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cert/PlacementNewStorageCheck.cpp
@@ -0,0 +1,180 @@
+//===--- PlacementNewStorageCheck.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 "PlacementNewStorageCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+static const ValueDecl *getDescendantValueDecl(const Stmt *TheStmt) {
+ if (const auto *TheDeclRefExpr = dyn_cast<DeclRefExpr>(TheStmt))
+ return TheDeclRefExpr->getDecl();
+
+ for (const Stmt *Child : TheStmt->children()) {
+ if (const auto *TheDeclRefExpr = dyn_cast<DeclRefExpr>(Child))
+ return TheDeclRefExpr->getDecl();
+
+ if (const ValueDecl *TheValueDeclExpr = getDescendantValueDecl(Child))
+ return TheValueDeclExpr;
+ }
+
+ return nullptr;
+}
+
+static void unwindPointeeType(QualType &Type) {
+ if (Type->isPointerType())
+ Type = Type->getPointeeType();
+ else if (Type->isReferenceType())
+ Type = Type->getPointeeType();
+
+ if (Type->isPointerType() || Type->isReferenceType())
+ unwindPointeeType(Type);
+}
+
+
+void PlacementNewStorageCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxNewExpr(hasPlacementArg(0, implicitCastExpr(expr().bind("arg0"))))
+ .bind("new"),
+ this);
+}
+
+void PlacementNewStorageCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *NewExpr = Result.Nodes.getNodeAs<CXXNewExpr>("new");
+ const auto *NewExprArg0 = Result.Nodes.getNodeAs<ImplicitCastExpr>("arg0");
+
+ const ASTContext *Context = Result.Context;
+ const SourceManager *SourceManager = Result.SourceManager;
+ QualType AllocatedT = NewExpr->getAllocatedType();
+
+ auto CheckStorageTypeIsFine =
+ [this, &AllocatedT, NewExpr, Context,
+ SourceManager](const ValueDecl *StorageVD) -> void {
+ QualType StorageT = StorageVD->getType();
+
+ if (const auto *TheFunctionProtoType = StorageT->getAs<FunctionProtoType>())
+ StorageT = TheFunctionProtoType->getReturnType();
+ else if (const auto *ThePointerType = StorageT->getAs<PointerType>())
+ if (const auto *TheFunctionProtoType =
+ ThePointerType->getPointeeType()->getAs<FunctionProtoType>())
+ StorageT = TheFunctionProtoType->getReturnType();
+
+ unwindPointeeType(StorageT);
+
+ if (StorageT->isVoidPointerType() || StorageT->isVoidType())
+ return;
+
+ const std::size_t BitsPerByteCount = 8;
+
+ unsigned StorageTAlign = Context->getTypeAlign(StorageT);
+ if (unsigned SpecifiedAlignment = StorageVD->getMaxAlignment())
+ StorageTAlign = SpecifiedAlignment;
+
+ StorageTAlign = StorageTAlign / BitsPerByteCount;
+ unsigned AllocatedTAlign =
+ Context->getTypeAlign(AllocatedT) / BitsPerByteCount;
+
+ if (AllocatedTAlign > StorageTAlign) {
+ diag(NewExpr->getBeginLoc(), "%0 is aligned to %1 bytes but "
+ "allocated type %2 is aligned to %3 bytes")
+ << StorageVD << StorageTAlign << AllocatedT << AllocatedTAlign;
+
+ return;
+ }
+
+ uint64_t StorageTSize = Context->getTypeSize(StorageT) / BitsPerByteCount;
+
+ if (llvm::Optional<const Expr *> TheArraySizeExpr =
+ NewExpr->getArraySize()) {
+ Expr::EvalResult EvaluatedArraySizeValue;
+ if (!TheArraySizeExpr.getValue()->EvaluateAsInt(EvaluatedArraySizeValue,
+ *Context) &&
+ !EvaluatedArraySizeValue.Val.isInt()) {
+ return;
+ }
+
+ uint64_t AllocatedSize =
+ (Context->getTypeSize(AllocatedT) *
+ (*EvaluatedArraySizeValue.Val.getInt().getRawData())) /
+ BitsPerByteCount;
+
+ if (AllocatedSize >= StorageTSize)
+ diag(NewExpr->getBeginLoc(), "%0 bytes are not enough for array "
+ "allocation which requires %1 bytes")
+ << (unsigned)AllocatedSize << (unsigned)StorageTSize;
+ else
+ diag(NewExpr->getBeginLoc(),
+ "possibly not enough %0 bytes for array allocation which requires "
+ "%1 bytes. current overhead requires the size of %2 bytes")
+ << (unsigned)StorageTSize << (unsigned)AllocatedSize
+ << (unsigned)(StorageTSize - AllocatedSize);
+ } else if (StorageT->isArrayType()) {
+ uint64_t AllocatedSize =
+ Context->getTypeSize(AllocatedT) / BitsPerByteCount;
+
+ if (AllocatedSize > StorageTSize)
+ diag(NewExpr->getBeginLoc(), "%0 bytes are not enough for allocation "
+ "type %1 which requires %2 bytes")
+ << (unsigned)StorageTSize << AllocatedT << (unsigned)AllocatedSize;
+ }
+ };
+
+ auto IsPointerAlignedProperly =
+ [this, &AllocatedT, NewExpr, Context,
+ SourceManager](const Expr *ThePointerAddressExpr) -> void {
+ Expr::EvalResult EvaluatedValue;
+ if (ThePointerAddressExpr->EvaluateAsInt(EvaluatedValue, *Context) &&
+ EvaluatedValue.Val.isInt()) {
+ unsigned AllocatedTAlign = Context->getTypeAlign(AllocatedT) / 8;
+ unsigned AddressAlign =
+ *EvaluatedValue.Val.getInt().getRawData() % AllocatedTAlign;
+ if (AddressAlign != 0) {
+ diag(NewExpr->getBeginLoc(),
+ "address is aligned to %0 bytes instead of %1 bytes")
+ << AddressAlign << AllocatedTAlign;
+ }
+ }
+ };
+
+ const Expr *StorageExpr = NewExprArg0->getSubExpr();
+
+ if (const auto *TheUnaryOperator = dyn_cast<UnaryOperator>(StorageExpr)) {
+ if (UnaryOperatorKind::UO_AddrOf == TheUnaryOperator->getOpcode()) {
+ if (const ValueDecl *TheValueDecl =
+ getDescendantValueDecl(TheUnaryOperator->getSubExpr())) {
+ CheckStorageTypeIsFine(TheValueDecl);
+ }
+ }
+ } else if (const auto *TheCallExpr = dyn_cast<CallExpr>(StorageExpr)) {
+ if (const ValueDecl *TheValueDecl = getDescendantValueDecl(TheCallExpr)) {
+ CheckStorageTypeIsFine(TheValueDecl);
+ }
+ } else if (const auto *TheCastExpr = dyn_cast<CastExpr>(StorageExpr)) {
+ const CastKind TheCastKind = TheCastExpr->getCastKind();
+ if (CastKind::CK_LValueToRValue == TheCastKind ||
+ CastKind::CK_ArrayToPointerDecay == TheCastKind) {
+ if (const ValueDecl *TheValueDecl =
+ getDescendantValueDecl(TheCastExpr->getSubExpr())) {
+ CheckStorageTypeIsFine(TheValueDecl);
+ }
+ } else if (CastKind::CK_IntegralToPointer == TheCastKind) {
+ IsPointerAlignedProperly(TheCastExpr->getSubExpr()->IgnoreParenCasts());
+ }
+ } else if (const auto *TheDeclRefExpr = dyn_cast<DeclRefExpr>(StorageExpr)) {
+ CheckStorageTypeIsFine(TheDeclRefExpr->getDecl());
+ }
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/cert/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cert/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/cert/CMakeLists.txt
@@ -9,6 +9,7 @@
LimitedRandomnessCheck.cpp
MutatingCopyCheck.cpp
NonTrivialTypesLibcMemoryCallsCheck.cpp
+ PlacementNewStorageCheck.cpp
PostfixOperatorCheck.cpp
ProperlySeededRandomGeneratorCheck.cpp
SetLongJmpCheck.cpp
Index: clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
@@ -26,6 +26,7 @@
#include "LimitedRandomnessCheck.h"
#include "MutatingCopyCheck.h"
#include "NonTrivialTypesLibcMemoryCallsCheck.h"
+#include "PlacementNewStorageCheck.h"
#include "PostfixOperatorCheck.h"
#include "ProperlySeededRandomGeneratorCheck.h"
#include "SetLongJmpCheck.h"
@@ -63,6 +64,7 @@
CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
"cert-err61-cpp");
// MEM
+ CheckFactories.registerCheck<PlacementNewStorageCheck>("cert-mem54-cpp");
CheckFactories.registerCheck<DefaultOperatorNewAlignmentCheck>(
"cert-mem57-cpp");
// MSC
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits