mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-02 15:26:29 +00:00
[clang-tidy] Add bugprone-suspicious-memory-comparison check
The check warns on suspicious calls to `memcmp`. It currently checks for comparing types that do not have unique object representations or are non-standard-layout. Based on https://wiki.sei.cmu.edu/confluence/display/c/EXP42-C.+Do+not+compare+padding+data https://wiki.sei.cmu.edu/confluence/display/c/FLP37-C.+Do+not+use+object+representations+to+compare+floating-point+values and part of https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP57-CPP.+Prefer+special+member+functions+and+overloaded+operators+to+C+Standard+Library+functions Add alias `cert-exp42-c` and `cert-flp37-c`. Some tests are currently failing at head, the check depends on D89649. Originally started in D71973 Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D89651
This commit is contained in:
parent
ad59735f9d
commit
3373e84539
@ -52,6 +52,7 @@
|
||||
#include "StringLiteralWithEmbeddedNulCheck.h"
|
||||
#include "SuspiciousEnumUsageCheck.h"
|
||||
#include "SuspiciousIncludeCheck.h"
|
||||
#include "SuspiciousMemoryComparisonCheck.h"
|
||||
#include "SuspiciousMemsetUsageCheck.h"
|
||||
#include "SuspiciousMissingCommaCheck.h"
|
||||
#include "SuspiciousSemicolonCheck.h"
|
||||
@ -160,6 +161,8 @@ public:
|
||||
"bugprone-suspicious-enum-usage");
|
||||
CheckFactories.registerCheck<SuspiciousIncludeCheck>(
|
||||
"bugprone-suspicious-include");
|
||||
CheckFactories.registerCheck<SuspiciousMemoryComparisonCheck>(
|
||||
"bugprone-suspicious-memory-comparison");
|
||||
CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
|
||||
"bugprone-suspicious-memset-usage");
|
||||
CheckFactories.registerCheck<SuspiciousMissingCommaCheck>(
|
||||
|
@ -47,6 +47,7 @@ add_clang_library(clangTidyBugproneModule
|
||||
StringLiteralWithEmbeddedNulCheck.cpp
|
||||
SuspiciousEnumUsageCheck.cpp
|
||||
SuspiciousIncludeCheck.cpp
|
||||
SuspiciousMemoryComparisonCheck.cpp
|
||||
SuspiciousMemsetUsageCheck.cpp
|
||||
SuspiciousMissingCommaCheck.cpp
|
||||
SuspiciousSemicolonCheck.cpp
|
||||
|
@ -0,0 +1,85 @@
|
||||
//===--- SuspiciousMemoryComparisonCheck.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 "SuspiciousMemoryComparisonCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
static llvm::Optional<uint64_t> tryEvaluateSizeExpr(const Expr *SizeExpr,
|
||||
const ASTContext &Ctx) {
|
||||
Expr::EvalResult Result;
|
||||
if (SizeExpr->EvaluateAsRValue(Result, Ctx))
|
||||
return Ctx.toBits(
|
||||
CharUnits::fromQuantity(Result.Val.getInt().getExtValue()));
|
||||
return None;
|
||||
}
|
||||
|
||||
void SuspiciousMemoryComparisonCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(
|
||||
callExpr(allOf(callee(namedDecl(
|
||||
anyOf(hasName("::memcmp"), hasName("::std::memcmp")))),
|
||||
unless(isInstantiationDependent())))
|
||||
.bind("call"),
|
||||
this);
|
||||
}
|
||||
|
||||
void SuspiciousMemoryComparisonCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
const ASTContext &Ctx = *Result.Context;
|
||||
const auto *CE = Result.Nodes.getNodeAs<CallExpr>("call");
|
||||
|
||||
const Expr *SizeExpr = CE->getArg(2);
|
||||
assert(SizeExpr != nullptr && "Third argument of memcmp is mandatory.");
|
||||
llvm::Optional<uint64_t> ComparedBits = tryEvaluateSizeExpr(SizeExpr, Ctx);
|
||||
|
||||
for (unsigned int ArgIndex = 0; ArgIndex < 2; ++ArgIndex) {
|
||||
const Expr *ArgExpr = CE->getArg(ArgIndex);
|
||||
QualType ArgType = ArgExpr->IgnoreImplicit()->getType();
|
||||
const Type *PointeeType = ArgType->getPointeeOrArrayElementType();
|
||||
assert(PointeeType != nullptr && "PointeeType should always be available.");
|
||||
QualType PointeeQualifiedType(PointeeType, 0);
|
||||
|
||||
if (PointeeType->isRecordType()) {
|
||||
if (const RecordDecl *RD =
|
||||
PointeeType->getAsRecordDecl()->getDefinition()) {
|
||||
if (const auto *CXXDecl = dyn_cast<CXXRecordDecl>(RD)) {
|
||||
if (!CXXDecl->isStandardLayout()) {
|
||||
diag(CE->getBeginLoc(),
|
||||
"comparing object representation of non-standard-layout type "
|
||||
"%0; consider using a comparison operator instead")
|
||||
<< PointeeQualifiedType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PointeeType->isIncompleteType()) {
|
||||
uint64_t PointeeSize = Ctx.getTypeSize(PointeeType);
|
||||
if (ComparedBits.hasValue() && *ComparedBits >= PointeeSize &&
|
||||
!Ctx.hasUniqueObjectRepresentations(PointeeQualifiedType)) {
|
||||
diag(CE->getBeginLoc(),
|
||||
"comparing object representation of type %0 which does not have a "
|
||||
"unique object representation; consider comparing %select{the "
|
||||
"values|the members of the object}1 manually")
|
||||
<< PointeeQualifiedType << (PointeeType->isRecordType() ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
@ -0,0 +1,35 @@
|
||||
//===--- SuspiciousMemoryComparisonCheck.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_SUSPICIOUSMEMORYCOMPARISONCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMEMORYCOMPARISONCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
/// Finds potentially incorrect calls to ``memcmp()`` based on properties of the
|
||||
/// arguments.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-memory-comparison.html
|
||||
class SuspiciousMemoryComparisonCheck : public ClangTidyCheck {
|
||||
public:
|
||||
SuspiciousMemoryComparisonCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMEMORYCOMPARISONCHECK_H
|
@ -14,6 +14,7 @@
|
||||
#include "../bugprone/SignalHandlerCheck.h"
|
||||
#include "../bugprone/SignedCharMisuseCheck.h"
|
||||
#include "../bugprone/SpuriouslyWakeUpFunctionsCheck.h"
|
||||
#include "../bugprone/SuspiciousMemoryComparisonCheck.h"
|
||||
#include "../bugprone/UnhandledSelfAssignmentCheck.h"
|
||||
#include "../concurrency/ThreadCanceltypeAsynchronousCheck.h"
|
||||
#include "../google/UnnamedNamespaceInHeaderCheck.h"
|
||||
@ -98,8 +99,13 @@ public:
|
||||
"cert-dcl37-c");
|
||||
// ENV
|
||||
CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c");
|
||||
// EXP
|
||||
CheckFactories.registerCheck<bugprone::SuspiciousMemoryComparisonCheck>(
|
||||
"cert-exp42-c");
|
||||
// FLP
|
||||
CheckFactories.registerCheck<FloatLoopCounter>("cert-flp30-c");
|
||||
CheckFactories.registerCheck<bugprone::SuspiciousMemoryComparisonCheck>(
|
||||
"cert-flp37-c");
|
||||
// FIO
|
||||
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
|
||||
// ERR
|
||||
|
@ -72,6 +72,11 @@ The improvements are...
|
||||
New checks
|
||||
^^^^^^^^^^
|
||||
|
||||
- New :doc:`bugprone-suspicious-memory-comparison
|
||||
<clang-tidy/checks/bugprone-suspicious-memory-comparison>` check.
|
||||
|
||||
Finds potentially incorrect calls to ``memcmp()`` based on properties of the arguments.
|
||||
|
||||
- New :doc:`readability-identifier-length
|
||||
<clang-tidy/checks/readability-identifier-length>` check.
|
||||
|
||||
@ -80,6 +85,16 @@ New checks
|
||||
New check aliases
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
- New alias :doc:`cert-exp42-c
|
||||
<clang-tidy/checks/cert-exp42-c>` to
|
||||
:doc:`bugprone-suspicious-memory-comparison
|
||||
<clang-tidy/checks/bugprone-suspicious-memory-comparison>` was added.
|
||||
|
||||
- New alias :doc:`cert-flp37-c
|
||||
<clang-tidy/checks/cert-flp37-c>` to
|
||||
:doc:`bugprone-suspicious-memory-comparison
|
||||
<clang-tidy/checks/bugprone-suspicious-memory-comparison>` was added.
|
||||
|
||||
Changes in existing checks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
.. title:: clang-tidy - bugprone-suspicious-memory-comparison
|
||||
|
||||
bugprone-suspicious-memory-comparison
|
||||
=====================================
|
||||
|
||||
Finds potentially incorrect calls to ``memcmp()`` based on properties of the
|
||||
arguments. The following cases are covered:
|
||||
|
||||
**Case 1: Non-standard-layout type**
|
||||
|
||||
Comparing the object representaions of non-standard-layout objects may not
|
||||
properly compare the value representations.
|
||||
|
||||
**Case 2: Types with no unique object representation**
|
||||
|
||||
Objects with the same value may not have the same object representaion.
|
||||
This may be caused by padding or floating-point types.
|
||||
|
||||
See also:
|
||||
`EXP42-C. Do not compare padding data
|
||||
<https://wiki.sei.cmu.edu/confluence/display/c/EXP42-C.+Do+not+compare+padding+data>`_
|
||||
and
|
||||
`FLP37-C. Do not use object representations to compare floating-point values
|
||||
<https://wiki.sei.cmu.edu/confluence/display/c/FLP37-C.+Do+not+use+object+representations+to+compare+floating-point+values>`_
|
||||
|
||||
This check is also related to and partially overlaps the CERT C++ Coding Standard rules
|
||||
`OOP57-CPP. Prefer special member functions and overloaded operators to C Standard Library functions
|
||||
<https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP57-CPP.+Prefer+special+member+functions+and+overloaded+operators+to+C+Standard+Library+functions>`_
|
||||
and
|
||||
`EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation
|
||||
<https://wiki.sei.cmu.edu/confluence/display/cplusplus/EXP62-CPP.+Do+not+access+the+bits+of+an+object+representation+that+are+not+part+of+the+object%27s+value+representation>`_
|
@ -0,0 +1,8 @@
|
||||
.. meta::
|
||||
:http-equiv=refresh: 5;URL=bugprone-suspicious-memory-comparison.html
|
||||
|
||||
cert-exp42-c
|
||||
============
|
||||
|
||||
The cert-exp42-c check is an alias, please see
|
||||
`bugprone-suspicious-memory-comparison <bugprone-suspicious-memory-comparison.html>`_ for more information.
|
@ -0,0 +1,8 @@
|
||||
.. meta::
|
||||
:http-equiv=refresh: 5;URL=bugprone-suspicious-memory-comparison.html
|
||||
|
||||
cert-flp37-c
|
||||
============
|
||||
|
||||
The cert-flp37-c check is an alias, please see
|
||||
`bugprone-suspicious-memory-comparison <bugprone-suspicious-memory-comparison.html>`_ for more information.
|
@ -93,6 +93,7 @@ Clang-Tidy Checks
|
||||
`bugprone-string-literal-with-embedded-nul <bugprone-string-literal-with-embedded-nul.html>`_,
|
||||
`bugprone-suspicious-enum-usage <bugprone-suspicious-enum-usage.html>`_,
|
||||
`bugprone-suspicious-include <bugprone-suspicious-include.html>`_,
|
||||
`bugprone-suspicious-memory-comparison <bugprone-suspicious-memory-comparison.html>`_,
|
||||
`bugprone-suspicious-memset-usage <bugprone-suspicious-memset-usage.html>`_, "Yes"
|
||||
`bugprone-suspicious-missing-comma <bugprone-suspicious-missing-comma.html>`_,
|
||||
`bugprone-suspicious-semicolon <bugprone-suspicious-semicolon.html>`_, "Yes"
|
||||
@ -117,7 +118,9 @@ Clang-Tidy Checks
|
||||
`cert-err52-cpp <cert-err52-cpp.html>`_,
|
||||
`cert-err58-cpp <cert-err58-cpp.html>`_,
|
||||
`cert-err60-cpp <cert-err60-cpp.html>`_,
|
||||
`cert-exp42-c <cert-exp42-c.html>`_,
|
||||
`cert-flp30-c <cert-flp30-c.html>`_,
|
||||
`cert-flp37-c <cert-flp37-c.html>`_,
|
||||
`cert-mem57-cpp <cert-mem57-cpp.html>`_,
|
||||
`cert-msc50-cpp <cert-msc50-cpp.html>`_,
|
||||
`cert-msc51-cpp <cert-msc51-cpp.html>`_,
|
||||
|
@ -0,0 +1,33 @@
|
||||
// RUN: %check_clang_tidy %s bugprone-suspicious-memory-comparison %t \
|
||||
// RUN: -- -- -target i386-unknown-unknown
|
||||
|
||||
static_assert(sizeof(int *) == sizeof(int));
|
||||
|
||||
namespace std {
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
int memcmp(const void *lhs, const void *rhs, size_t count);
|
||||
} // namespace std
|
||||
|
||||
namespace no_padding_on_32bit {
|
||||
struct S {
|
||||
int x;
|
||||
int *y;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
}
|
||||
} // namespace no_padding_on_32bit
|
||||
|
||||
namespace inner_padding {
|
||||
struct S {
|
||||
char x;
|
||||
int y;
|
||||
};
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'inner_padding::S' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
} // namespace inner_padding
|
@ -0,0 +1,294 @@
|
||||
// RUN: %check_clang_tidy %s bugprone-suspicious-memory-comparison %t \
|
||||
// RUN: -- -- -target x86_64-unknown-unknown -std=c99
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
int memcmp(const void *lhs, const void *rhs, size_t count);
|
||||
|
||||
// Examples from cert rule exp42-c
|
||||
|
||||
struct S {
|
||||
char c;
|
||||
int i;
|
||||
char buffer[13];
|
||||
};
|
||||
|
||||
void exp42_c_noncompliant(const struct S *left, const struct S *right) {
|
||||
if ((left && right) && (0 == memcmp(left, right, sizeof(struct S)))) {
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: comparing object representation of type 'struct S' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
}
|
||||
|
||||
void exp42_c_compliant(const struct S *left, const struct S *right) {
|
||||
if ((left && right) && (left->c == right->c) && (left->i == right->i) &&
|
||||
(0 == memcmp(left->buffer, right->buffer, 13))) {
|
||||
}
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Packed_S {
|
||||
char c;
|
||||
int i;
|
||||
char buffer[13];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
void compliant_packed(const struct Packed_S *left,
|
||||
const struct Packed_S *right) {
|
||||
if ((left && right) && (0 == memcmp(left, right, sizeof(struct Packed_S)))) {
|
||||
// no-warning
|
||||
}
|
||||
}
|
||||
|
||||
// Examples from cert rule flp37-c
|
||||
|
||||
struct S2 {
|
||||
int i;
|
||||
float f;
|
||||
};
|
||||
|
||||
int flp37_c_noncompliant(const struct S2 *s1, const struct S2 *s2) {
|
||||
if (!s1 && !s2)
|
||||
return 1;
|
||||
else if (!s1 || !s2)
|
||||
return 0;
|
||||
return 0 == memcmp(s1, s2, sizeof(struct S2));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: comparing object representation of type 'struct S2' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
int flp37_c_compliant(const struct S2 *s1, const struct S2 *s2) {
|
||||
if (!s1 && !s2)
|
||||
return 1;
|
||||
else if (!s1 || !s2)
|
||||
return 0;
|
||||
return s1->i == s2->i && s1->f == s2->f;
|
||||
// no-warning
|
||||
}
|
||||
|
||||
void Test_Float() {
|
||||
float a, b;
|
||||
memcmp(&a, &b, sizeof(float));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'float' which does not have a unique object representation; consider comparing the values manually
|
||||
}
|
||||
|
||||
void TestArray_Float() {
|
||||
float a[3], b[3];
|
||||
memcmp(a, b, sizeof(a));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'float' which does not have a unique object representation; consider comparing the values manually
|
||||
}
|
||||
|
||||
struct PredeclaredType;
|
||||
|
||||
void Test_PredeclaredType(const struct PredeclaredType *lhs,
|
||||
const struct PredeclaredType *rhs) {
|
||||
memcmp(lhs, rhs, 1); // no-warning: predeclared type
|
||||
}
|
||||
|
||||
struct NoPadding {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
void Test_NoPadding() {
|
||||
struct NoPadding a, b;
|
||||
memcmp(&a, &b, sizeof(struct NoPadding));
|
||||
}
|
||||
|
||||
void TestArray_NoPadding() {
|
||||
struct NoPadding a[3], b[3];
|
||||
memcmp(a, b, 3 * sizeof(struct NoPadding));
|
||||
}
|
||||
|
||||
struct TrailingPadding {
|
||||
int i;
|
||||
char c;
|
||||
};
|
||||
|
||||
void Test_TrailingPadding() {
|
||||
struct TrailingPadding a, b;
|
||||
memcmp(&a, &b, sizeof(struct TrailingPadding));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct TrailingPadding' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
memcmp(&a, &b, sizeof(int)); // no-warning: not comparing entire object
|
||||
memcmp(&a, &b, 2 * sizeof(int));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct TrailingPadding' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct TrailingPadding2 {
|
||||
int i[2];
|
||||
char c;
|
||||
};
|
||||
|
||||
void Test_TrailingPadding2() {
|
||||
struct TrailingPadding2 a, b;
|
||||
memcmp(&a, &b, 2 * sizeof(int)); // no-warning: not comparing entire object
|
||||
memcmp(&a, &b, sizeof(struct TrailingPadding2));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct TrailingPadding2' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
void Test_UnknownCount(size_t count) {
|
||||
struct TrailingPadding a, b;
|
||||
memcmp(&a, &b, count); // no-warning: unknown count value
|
||||
}
|
||||
|
||||
void Test_ExplicitVoidCast() {
|
||||
struct TrailingPadding a, b;
|
||||
memcmp((void *)&a, (void *)&b,
|
||||
sizeof(struct TrailingPadding)); // no-warning: explicit cast
|
||||
}
|
||||
|
||||
void TestArray_TrailingPadding() {
|
||||
struct TrailingPadding a[3], b[3];
|
||||
memcmp(a, b, 3 * sizeof(struct TrailingPadding));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct TrailingPadding' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct InnerPadding {
|
||||
char c;
|
||||
int i;
|
||||
};
|
||||
|
||||
void Test_InnerPadding() {
|
||||
struct InnerPadding a, b;
|
||||
memcmp(&a, &b, sizeof(struct InnerPadding));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct InnerPadding' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct Bitfield_TrailingPaddingBytes {
|
||||
int x : 10;
|
||||
int y : 6;
|
||||
};
|
||||
|
||||
void Test_Bitfield_TrailingPaddingBytes() {
|
||||
struct Bitfield_TrailingPaddingBytes a, b;
|
||||
memcmp(&a, &b, sizeof(struct S));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct Bitfield_TrailingPaddingBytes' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct Bitfield_TrailingPaddingBits {
|
||||
int x : 10;
|
||||
int y : 20;
|
||||
};
|
||||
|
||||
void Test_Bitfield_TrailingPaddingBits() {
|
||||
struct Bitfield_TrailingPaddingBits a, b;
|
||||
memcmp(&a, &b, sizeof(struct Bitfield_TrailingPaddingBits));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct Bitfield_TrailingPaddingBits' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct Bitfield_InnerPaddingBits {
|
||||
char x : 2;
|
||||
int : 0;
|
||||
char y : 8;
|
||||
};
|
||||
|
||||
void Test_Bitfield_InnerPaddingBits() {
|
||||
struct Bitfield_InnerPaddingBits a, b;
|
||||
memcmp(&a, &b, sizeof(struct Bitfield_InnerPaddingBits));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct Bitfield_InnerPaddingBits' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct Bitfield_NoPadding {
|
||||
int i : 10;
|
||||
int j : 10;
|
||||
int k : 10;
|
||||
int l : 2;
|
||||
};
|
||||
_Static_assert(sizeof(struct Bitfield_NoPadding) == sizeof(int),
|
||||
"Bit-fields should line up perfectly");
|
||||
|
||||
void Test_Bitfield_NoPadding() {
|
||||
struct Bitfield_NoPadding a, b;
|
||||
memcmp(&a, &b, sizeof(struct Bitfield_NoPadding)); // no-warning
|
||||
}
|
||||
|
||||
struct Bitfield_TrailingUnnamed {
|
||||
int i[2];
|
||||
int : 0;
|
||||
};
|
||||
|
||||
void Bitfield_TrailingUnnamed() {
|
||||
struct Bitfield_TrailingUnnamed a, b;
|
||||
memcmp(&a, &b, 2 * sizeof(int)); // no-warning
|
||||
memcmp(&a, &b, sizeof(struct Bitfield_TrailingUnnamed)); // no-warning
|
||||
}
|
||||
|
||||
struct PaddingAfterUnion {
|
||||
union {
|
||||
unsigned short a;
|
||||
short b;
|
||||
} x;
|
||||
|
||||
int y;
|
||||
};
|
||||
|
||||
void Test_PaddingAfterUnion() {
|
||||
struct PaddingAfterUnion a, b;
|
||||
memcmp(&a, &b, sizeof(struct PaddingAfterUnion));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct PaddingAfterUnion' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct Union_NoPadding {
|
||||
union {
|
||||
int a;
|
||||
unsigned int b;
|
||||
} x;
|
||||
|
||||
int y;
|
||||
};
|
||||
|
||||
void Test_Union_NoPadding() {
|
||||
struct Union_NoPadding a, b;
|
||||
memcmp(&a, &b, 2 * sizeof(int));
|
||||
memcmp(&a, &b, sizeof(struct Union_NoPadding));
|
||||
}
|
||||
|
||||
union UnionWithPaddingInNestedStruct {
|
||||
int i;
|
||||
|
||||
struct {
|
||||
int i;
|
||||
char c;
|
||||
} x;
|
||||
};
|
||||
|
||||
void Test_UnionWithPaddingInNestedStruct() {
|
||||
union UnionWithPaddingInNestedStruct a, b;
|
||||
memcmp(&a, &b, sizeof(union UnionWithPaddingInNestedStruct));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'union UnionWithPaddingInNestedStruct' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct PaddingInNested {
|
||||
struct TrailingPadding x;
|
||||
char y;
|
||||
};
|
||||
|
||||
void Test_PaddingInNested() {
|
||||
struct PaddingInNested a, b;
|
||||
memcmp(&a, &b, sizeof(struct PaddingInNested));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct PaddingInNested' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct PaddingAfterNested {
|
||||
struct {
|
||||
char a;
|
||||
char b;
|
||||
} x;
|
||||
int y;
|
||||
};
|
||||
|
||||
void Test_PaddingAfterNested() {
|
||||
struct PaddingAfterNested a, b;
|
||||
memcmp(&a, &b, sizeof(struct PaddingAfterNested));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct PaddingAfterNested' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
struct AtomicMember {
|
||||
_Atomic(int) x;
|
||||
};
|
||||
|
||||
void Test_AtomicMember() {
|
||||
// FIXME: this is a false positive as the list of objects with unique object
|
||||
// representations is incomplete.
|
||||
struct AtomicMember a, b;
|
||||
memcmp(&a, &b, sizeof(struct AtomicMember));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'struct AtomicMember' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
// RUN: %check_clang_tidy %s bugprone-suspicious-memory-comparison %t \
|
||||
// RUN: -- -- -target x86_64-unknown-unknown
|
||||
|
||||
namespace std {
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
int memcmp(const void *lhs, const void *rhs, size_t count);
|
||||
} // namespace std
|
||||
|
||||
namespace sei_cert_example_oop57_cpp {
|
||||
class C {
|
||||
int i;
|
||||
|
||||
public:
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
void f(C &c1, C &c2) {
|
||||
if (!std::memcmp(&c1, &c2, sizeof(C))) {
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: comparing object representation of non-standard-layout type 'sei_cert_example_oop57_cpp::C'; consider using a comparison operator instead
|
||||
}
|
||||
}
|
||||
} // namespace sei_cert_example_oop57_cpp
|
||||
|
||||
namespace inner_padding_64bit_only {
|
||||
struct S {
|
||||
int x;
|
||||
int *y;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'inner_padding_64bit_only::S' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
} // namespace inner_padding_64bit_only
|
||||
|
||||
namespace padding_in_base {
|
||||
class Base {
|
||||
char c;
|
||||
int i;
|
||||
};
|
||||
|
||||
class Derived : public Base {};
|
||||
|
||||
class Derived2 : public Derived {};
|
||||
|
||||
void testDerived() {
|
||||
Derived a, b;
|
||||
std::memcmp(&a, &b, sizeof(Base));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'padding_in_base::Derived' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
std::memcmp(&a, &b, sizeof(Derived));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'padding_in_base::Derived' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
void testDerived2() {
|
||||
Derived2 a, b;
|
||||
std::memcmp(&a, &b, sizeof(Base));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'padding_in_base::Derived2' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
std::memcmp(&a, &b, sizeof(Derived2));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'padding_in_base::Derived2' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
} // namespace padding_in_base
|
||||
|
||||
namespace no_padding_in_base {
|
||||
class Base {
|
||||
int a, b;
|
||||
};
|
||||
|
||||
class Derived : public Base {};
|
||||
|
||||
class Derived2 : public Derived {};
|
||||
|
||||
void testDerived() {
|
||||
Derived a, b;
|
||||
std::memcmp(&a, &b, sizeof(Base));
|
||||
std::memcmp(&a, &b, sizeof(Derived));
|
||||
}
|
||||
|
||||
void testDerived2() {
|
||||
Derived2 a, b;
|
||||
std::memcmp(&a, &b, sizeof(char));
|
||||
std::memcmp(&a, &b, sizeof(Base));
|
||||
std::memcmp(&a, &b, sizeof(Derived2));
|
||||
}
|
||||
} // namespace no_padding_in_base
|
||||
|
||||
namespace non_standard_layout {
|
||||
class C {
|
||||
private:
|
||||
int x;
|
||||
|
||||
public:
|
||||
int y;
|
||||
};
|
||||
|
||||
void test() {
|
||||
C a, b;
|
||||
std::memcmp(&a, &b, sizeof(C));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of non-standard-layout type 'non_standard_layout::C'; consider using a comparison operator instead
|
||||
}
|
||||
|
||||
} // namespace non_standard_layout
|
||||
|
||||
namespace static_ignored {
|
||||
struct S {
|
||||
static char c;
|
||||
int i;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
}
|
||||
} // namespace static_ignored
|
||||
|
||||
namespace operator_void_ptr {
|
||||
struct S {
|
||||
operator void *() const;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S s;
|
||||
std::memcmp(s, s, sizeof(s));
|
||||
}
|
||||
} // namespace operator_void_ptr
|
||||
|
||||
namespace empty_struct {
|
||||
struct S {};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'empty_struct::S' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
} // namespace empty_struct
|
||||
|
||||
namespace empty_field {
|
||||
struct Empty {};
|
||||
struct S {
|
||||
Empty e;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'empty_field::S' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
} // namespace empty_field
|
||||
|
||||
namespace no_unique_address_attribute {
|
||||
struct Empty {};
|
||||
|
||||
namespace no_padding {
|
||||
struct S {
|
||||
char c;
|
||||
[[no_unique_address]] Empty e;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
}
|
||||
|
||||
} // namespace no_padding
|
||||
|
||||
namespace multiple_empties_same_type {
|
||||
struct S {
|
||||
char c;
|
||||
[[no_unique_address]] Empty e1, e2;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'no_unique_address_attribute::multiple_empties_same_type::S' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
|
||||
} // namespace multiple_empties_same_type
|
||||
|
||||
namespace multiple_empties_different_types {
|
||||
struct Empty2 {};
|
||||
|
||||
struct S {
|
||||
char c;
|
||||
[[no_unique_address]] Empty e1;
|
||||
[[no_unique_address]] Empty2 e2;
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
}
|
||||
} // namespace multiple_empties_different_types
|
||||
} // namespace no_unique_address_attribute
|
||||
|
||||
namespace alignment {
|
||||
struct S {
|
||||
char x;
|
||||
alignas(sizeof(int)) char y[sizeof(int)];
|
||||
};
|
||||
|
||||
void test() {
|
||||
S a, b;
|
||||
std::memcmp(&a, &b, sizeof(S));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of type 'alignment::S' which does not have a unique object representation; consider comparing the members of the object manually
|
||||
}
|
||||
} // namespace alignment
|
||||
|
||||
namespace no_warning_in_template {
|
||||
template <typename T>
|
||||
int compare(const T *l, const T *r) {
|
||||
return std::memcmp(l, r, sizeof(T));
|
||||
}
|
||||
|
||||
void test() {
|
||||
int a, b;
|
||||
compare(&a, &b);
|
||||
}
|
||||
} // namespace no_warning_in_template
|
||||
|
||||
namespace warning_in_template {
|
||||
template <typename T>
|
||||
int compare(const T *l, const T *r) {
|
||||
return std::memcmp(l, r, sizeof(T));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: comparing object representation of type 'float' which does not have a unique object representation; consider comparing the values manually
|
||||
}
|
||||
|
||||
void test() {
|
||||
float a, b;
|
||||
compare(&a, &b);
|
||||
}
|
||||
} // namespace warning_in_template
|
Loading…
x
Reference in New Issue
Block a user