Skip to content

Commit f788a4d

Browse files
committed
Merge from 'main' to 'sycl-web' (59 commits)
CONFLICT (content): Merge conflict in llvm/CMakeLists.txt
2 parents 143db5e + 258477e commit f788a4d

File tree

400 files changed

+10030
-6243
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

400 files changed

+10030
-6243
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "SuspiciousMemoryComparisonCheck.h"
5959
#include "SuspiciousMemsetUsageCheck.h"
6060
#include "SuspiciousMissingCommaCheck.h"
61+
#include "SuspiciousReallocUsageCheck.h"
6162
#include "SuspiciousSemicolonCheck.h"
6263
#include "SuspiciousStringCompareCheck.h"
6364
#include "SwappedArgumentsCheck.h"
@@ -173,6 +174,8 @@ class BugproneModule : public ClangTidyModule {
173174
"bugprone-suspicious-memset-usage");
174175
CheckFactories.registerCheck<SuspiciousMissingCommaCheck>(
175176
"bugprone-suspicious-missing-comma");
177+
CheckFactories.registerCheck<SuspiciousReallocUsageCheck>(
178+
"bugprone-suspicious-realloc-usage");
176179
CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
177180
"bugprone-suspicious-semicolon");
178181
CheckFactories.registerCheck<SuspiciousStringCompareCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ add_clang_library(clangTidyBugproneModule
5454
SuspiciousMemoryComparisonCheck.cpp
5555
SuspiciousMemsetUsageCheck.cpp
5656
SuspiciousMissingCommaCheck.cpp
57+
SuspiciousReallocUsageCheck.cpp
5758
SuspiciousSemicolonCheck.cpp
5859
SuspiciousStringCompareCheck.cpp
5960
SwappedArgumentsCheck.cpp
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
//===--- SuspiciousReallocUsageCheck.cpp - clang-tidy----------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "SuspiciousReallocUsageCheck.h"
10+
#include "../utils/Aliasing.h"
11+
#include "clang/AST/ASTContext.h"
12+
#include "clang/AST/DeclVisitor.h"
13+
#include "clang/AST/StmtVisitor.h"
14+
#include "clang/ASTMatchers/ASTMatchFinder.h"
15+
#include "clang/ASTMatchers/ASTMatchers.h"
16+
#include "clang/Lex/Lexer.h"
17+
18+
using namespace clang::ast_matchers;
19+
using namespace clang;
20+
21+
namespace {
22+
/// Check if two different expression nodes denote the same
23+
/// "pointer expression". The "pointer expression" can consist of member
24+
/// expressions and declaration references only (like \c a->b->c), otherwise the
25+
/// check is always false.
26+
class IsSamePtrExpr : public StmtVisitor<IsSamePtrExpr, bool> {
27+
/// The other expression to compare against.
28+
/// This variable is used to pass the data from a \c check function to any of
29+
/// the visit functions. Every visit function starts by converting \c OtherE
30+
/// to the current type and store it locally, and do not use \c OtherE later.
31+
const Expr *OtherE = nullptr;
32+
33+
public:
34+
bool VisitDeclRefExpr(const DeclRefExpr *E1) {
35+
const auto *E2 = dyn_cast<DeclRefExpr>(OtherE);
36+
if (!E2)
37+
return false;
38+
const Decl *D1 = E1->getDecl()->getCanonicalDecl();
39+
return isa<VarDecl, FieldDecl>(D1) &&
40+
D1 == E2->getDecl()->getCanonicalDecl();
41+
}
42+
43+
bool VisitMemberExpr(const MemberExpr *E1) {
44+
const auto *E2 = dyn_cast<MemberExpr>(OtherE);
45+
if (!E2)
46+
return false;
47+
if (!check(E1->getBase(), E2->getBase()))
48+
return false;
49+
DeclAccessPair FD = E1->getFoundDecl();
50+
return isa<FieldDecl>(FD.getDecl()) && FD == E2->getFoundDecl();
51+
}
52+
53+
bool check(const Expr *E1, const Expr *E2) {
54+
E1 = E1->IgnoreParenCasts();
55+
E2 = E2->IgnoreParenCasts();
56+
OtherE = E2;
57+
return Visit(const_cast<Expr *>(E1));
58+
}
59+
};
60+
61+
/// Check if there is an assignment or initialization that references a variable
62+
/// \c Var (at right-hand side) and is before \c VarRef in the source code.
63+
/// Only simple assignments like \code a = b \endcode are found.
64+
class FindAssignToVarBefore
65+
: public ConstStmtVisitor<FindAssignToVarBefore, bool> {
66+
const VarDecl *Var;
67+
const DeclRefExpr *VarRef;
68+
SourceManager &SM;
69+
70+
bool isAccessForVar(const Expr *E) const {
71+
if (const auto *DeclRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
72+
return DeclRef->getDecl() &&
73+
DeclRef->getDecl()->getCanonicalDecl() == Var &&
74+
SM.isBeforeInTranslationUnit(E->getBeginLoc(),
75+
VarRef->getBeginLoc());
76+
return false;
77+
}
78+
79+
public:
80+
FindAssignToVarBefore(const VarDecl *Var, const DeclRefExpr *VarRef,
81+
SourceManager &SM)
82+
: Var(Var->getCanonicalDecl()), VarRef(VarRef), SM(SM) {}
83+
84+
bool VisitDeclStmt(const DeclStmt *S) {
85+
for (const Decl *D : S->getDeclGroup())
86+
if (const auto *LeftVar = dyn_cast<VarDecl>(D))
87+
if (LeftVar->hasInit())
88+
return isAccessForVar(LeftVar->getInit());
89+
return false;
90+
}
91+
bool VisitBinaryOperator(const BinaryOperator *S) {
92+
if (S->getOpcode() == BO_Assign)
93+
return isAccessForVar(S->getRHS());
94+
return false;
95+
}
96+
bool VisitStmt(const Stmt *S) {
97+
for (const Stmt *Child : S->children())
98+
if (Visit(Child))
99+
return true;
100+
return false;
101+
}
102+
};
103+
104+
} // namespace
105+
106+
namespace clang {
107+
namespace tidy {
108+
namespace bugprone {
109+
110+
void SuspiciousReallocUsageCheck::registerMatchers(MatchFinder *Finder) {
111+
// void *realloc(void *ptr, size_t size);
112+
auto ReallocDecl =
113+
functionDecl(hasName("::realloc"), parameterCountIs(2),
114+
hasParameter(0, hasType(pointerType(pointee(voidType())))),
115+
hasParameter(1, hasType(isInteger())))
116+
.bind("realloc");
117+
118+
auto ReallocCall =
119+
callExpr(callee(ReallocDecl), hasArgument(0, expr().bind("ptr_input")),
120+
hasAncestor(functionDecl().bind("parent_function")))
121+
.bind("call");
122+
Finder->addMatcher(binaryOperator(hasOperatorName("="),
123+
hasLHS(expr().bind("ptr_result")),
124+
hasRHS(ignoringParenCasts(ReallocCall))),
125+
this);
126+
}
127+
128+
void SuspiciousReallocUsageCheck::check(
129+
const MatchFinder::MatchResult &Result) {
130+
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
131+
if (!Call)
132+
return;
133+
const auto *PtrInputExpr = Result.Nodes.getNodeAs<Expr>("ptr_input");
134+
const auto *PtrResultExpr = Result.Nodes.getNodeAs<Expr>("ptr_result");
135+
if (!PtrInputExpr || !PtrResultExpr)
136+
return;
137+
const auto *ReallocD = Result.Nodes.getNodeAs<Decl>("realloc");
138+
assert(ReallocD && "Value for 'realloc' should exist if 'call' was found.");
139+
SourceManager &SM = ReallocD->getASTContext().getSourceManager();
140+
141+
if (!IsSamePtrExpr{}.check(PtrInputExpr, PtrResultExpr))
142+
return;
143+
144+
if (const auto *DeclRef =
145+
dyn_cast<DeclRefExpr>(PtrInputExpr->IgnoreParenImpCasts()))
146+
if (const auto *Var = dyn_cast<VarDecl>(DeclRef->getDecl()))
147+
if (const auto *Func =
148+
Result.Nodes.getNodeAs<FunctionDecl>("parent_function"))
149+
if (FindAssignToVarBefore{Var, DeclRef, SM}.Visit(Func->getBody()))
150+
return;
151+
152+
StringRef CodeOfAssignedExpr = Lexer::getSourceText(
153+
CharSourceRange::getTokenRange(PtrResultExpr->getSourceRange()), SM,
154+
getLangOpts());
155+
diag(Call->getBeginLoc(), "'%0' may be set to null if 'realloc' fails, which "
156+
"may result in a leak of the original buffer")
157+
<< CodeOfAssignedExpr << PtrInputExpr->getSourceRange()
158+
<< PtrResultExpr->getSourceRange();
159+
}
160+
161+
} // namespace bugprone
162+
} // namespace tidy
163+
} // namespace clang
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===--- SuspiciousReallocUsageCheck.h - clang-tidy -------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSREALLOCUSAGECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSREALLOCUSAGECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang {
15+
namespace tidy {
16+
namespace bugprone {
17+
18+
/// Finds usages of ``realloc`` where the return value is assigned to the same
19+
/// variable as passed to the first argument.
20+
///
21+
/// For the user-facing documentation see:
22+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/suspicious-realloc-usage.html
23+
class SuspiciousReallocUsageCheck : public ClangTidyCheck {
24+
public:
25+
SuspiciousReallocUsageCheck(StringRef Name, ClangTidyContext *Context)
26+
: ClangTidyCheck(Name, Context) {}
27+
28+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
29+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
30+
};
31+
32+
} // namespace bugprone
33+
} // namespace tidy
34+
} // namespace clang
35+
36+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSREALLOCUSAGECHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ Improvements to clang-tidy
9999
New checks
100100
^^^^^^^^^^
101101

102+
- New :doc:`bugprone-suspicious-realloc-usage
103+
<clang-tidy/checks/bugprone/suspicious-realloc-usage>` check.
104+
105+
Finds usages of ``realloc`` where the return value is assigned to the
106+
same expression as passed to the first argument.
107+
102108
- New :doc:`cppcoreguidelines-avoid-const-or-ref-data-members
103109
<clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members>` check.
104110

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.. title:: clang-tidy - bugprone-suspicious-realloc-usage
2+
3+
bugprone-suspicious-realloc-usage
4+
=================================
5+
6+
This check finds usages of ``realloc`` where the return value is assigned to the
7+
same expression as passed to the first argument:
8+
``p = realloc(p, size);``
9+
The problem with this construct is that if ``realloc`` fails it returns a
10+
null pointer but does not deallocate the original memory. If no other variable
11+
is pointing to it, the original memory block is not available any more for the
12+
program to use or free. In either case ``p = realloc(p, size);`` indicates bad
13+
coding style and can be replaced by ``q = realloc(p, size);``.
14+
15+
The pointer expression (used at ``realloc``) can be a variable or a field member
16+
of a data structure, but can not contain function calls or unresolved types.
17+
18+
In obvious cases when the pointer used at realloc is assigned to another
19+
variable before the ``realloc`` call, no warning is emitted. This happens only
20+
if a simple expression in form of ``q = p`` or ``void *q = p`` is found in the
21+
same function where ``p = realloc(p, ...)`` is found. The assignment has to be
22+
before the call to realloc (but otherwise at any place) in the same function.
23+
This suppression works only if ``p`` is a single variable.
24+
25+
Examples:
26+
27+
.. code-block:: c++
28+
29+
struct A {
30+
void *p;
31+
};
32+
33+
A &getA();
34+
35+
void foo(void *p, A *a, int new_size) {
36+
p = realloc(p, new_size); // warning: 'p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer
37+
a->p = realloc(a->p, new_size); // warning: 'a->p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer
38+
getA().p = realloc(getA().p, new_size); // no warning
39+
}
40+
41+
void foo1(void *p, int new_size) {
42+
void *p1 = p;
43+
p = realloc(p, new_size); // no warning
44+
}

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ Clang-Tidy Checks
124124
`bugprone-suspicious-memory-comparison <bugprone/suspicious-memory-comparison.html>`_,
125125
`bugprone-suspicious-memset-usage <bugprone/suspicious-memset-usage.html>`_, "Yes"
126126
`bugprone-suspicious-missing-comma <bugprone/suspicious-missing-comma.html>`_,
127+
`bugprone-suspicious-realloc-usage <bugprone/suspicious-realloc-usage.html>`_,
127128
`bugprone-suspicious-semicolon <bugprone/suspicious-semicolon.html>`_, "Yes"
128129
`bugprone-suspicious-string-compare <bugprone/suspicious-string-compare.html>`_, "Yes"
129130
`bugprone-swapped-arguments <bugprone/swapped-arguments.html>`_, "Yes"
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// RUN: %check_clang_tidy %s bugprone-suspicious-realloc-usage %t
2+
3+
void *realloc(void *, __SIZE_TYPE__);
4+
5+
namespace std {
6+
using ::realloc;
7+
}
8+
9+
namespace n1 {
10+
void *p;
11+
}
12+
13+
namespace n2 {
14+
void *p;
15+
}
16+
17+
struct P {
18+
void *p;
19+
void *q;
20+
P *pp;
21+
void *&f();
22+
};
23+
24+
struct P1 {
25+
static void *p;
26+
static void *q;
27+
};
28+
29+
template<class>
30+
struct P2 {
31+
static void *p;
32+
static void *q;
33+
};
34+
35+
template<class A, class B>
36+
void templ(void *p) {
37+
A::p = realloc(A::p, 10);
38+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 'A::p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
39+
p = realloc(A::p, 10);
40+
A::q = realloc(A::p, 10);
41+
A::p = realloc(B::p, 10);
42+
P2<A>::p = realloc(P2<A>::p, 1);
43+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'P2<A>::p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
44+
P2<A>::p = realloc(P2<B>::p, 1);
45+
}
46+
47+
void *&getPtr();
48+
P &getP();
49+
50+
void warn(void *p, P *p1, int *pi) {
51+
p = realloc(p, 111);
52+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
53+
54+
p = std::realloc(p, sizeof(int));
55+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
56+
57+
p1->p = realloc(p1->p, 10);
58+
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'p1->p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
59+
60+
p1->pp->p = realloc(p1->pp->p, 10);
61+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'p1->pp->p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
62+
63+
pi = (int*)realloc(pi, 10);
64+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'pi' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
65+
66+
templ<P1, P2<int>>(p);
67+
}
68+
69+
void no_warn(void *p, P *p1, P *p2) {
70+
void *q = realloc(p, 10);
71+
q = realloc(p, 10);
72+
p1->q = realloc(p1->p, 10);
73+
p2->p = realloc(p1->p, 20);
74+
n1::p = realloc(n2::p, 30);
75+
p1->pp->p = realloc(p1->p, 10);
76+
getPtr() = realloc(getPtr(), 30);
77+
getP().p = realloc(getP().p, 20);
78+
p1->f() = realloc(p1->f(), 30);
79+
}
80+
81+
void no_warn_if_copy_exists_before1(void *p) {
82+
void *q = p;
83+
p = realloc(p, 111);
84+
}
85+
86+
void no_warn_if_copy_exists_before2(void *p, void *q) {
87+
q = p;
88+
p = realloc(p, 111);
89+
}
90+
91+
void *g_p;
92+
93+
void no_warn_if_copy_exists_before3() {
94+
void *q = g_p;
95+
g_p = realloc(g_p, 111);
96+
}
97+
98+
void warn_if_copy_exists_after(void *p) {
99+
p = realloc(p, 111);
100+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer [bugprone-suspicious-realloc-usage]
101+
void *q = p;
102+
}

0 commit comments

Comments
 (0)