Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
#include "SwappedArgumentsCheck.h"
#include "SwitchMissingDefaultCaseCheck.h"
#include "TaggedUnionMemberCountCheck.h"
#include "TaxiAsyncUseAfterFreeCheck.h"
#include "TerminatingContinueCheck.h"
#include "ThrowKeywordMissingCheck.h"
#include "TooSmallLoopVariableCheck.h"
Expand Down Expand Up @@ -151,6 +152,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-incorrect-enable-if");
CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>(
"bugprone-incorrect-enable-shared-from-this");
CheckFactories.registerCheck<TaxiAsyncUseAfterFreeCheck>(
"bugprone-taxi-async-use-after-free");
CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>(
"bugprone-unintended-char-ostream-output");
CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>(
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ add_clang_library(clangTidyBugproneModule STATIC
InaccurateEraseCheck.cpp
IncorrectEnableIfCheck.cpp
IncorrectEnableSharedFromThisCheck.cpp
TaxiAsyncUseAfterFreeCheck.cpp
UnintendedCharOstreamOutputCheck.cpp
ReturnConstRefFromParameterCheck.cpp
SuspiciousStringviewDataUsageCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//===--- TaxiAsyncUseAfterFreeCheck.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 "TaxiAsyncUseAfterFreeCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::bugprone {

void TaxiAsyncUseAfterFreeCheck::registerMatchers(MatchFinder* Finder) {
auto hasAsyncName = hasAnyName(
"Async", "AsyncNoSpan", "SharedAsyncNoSpan", "CriticalAsyncNoSpan",
"SharedCriticalAsyncNoSpan", "CriticalAsync", "SharedCriticalAsync");

Finder->addMatcher(
lambdaExpr(
hasParent(materializeTemporaryExpr(hasParent(callExpr(
hasParent(cxxMemberCallExpr(

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

callee(cxxMethodDecl(hasName("push_back"))),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

on(declRefExpr(hasDeclaration(varDecl().bind("tasks")))))),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

callee(functionDecl(hasAsyncName)))))))
.bind("lambda"),
this);
}

void TaxiAsyncUseAfterFreeCheck::check(const MatchFinder::MatchResult& Result) {
const auto* MatchedLambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
const auto* MatchedTasks = Result.Nodes.getNodeAs<VarDecl>("tasks");
const SourceLocation TasksLocation = MatchedTasks->getLocation();

for (const auto& Capture : MatchedLambda->captures()) {
if (!Capture.capturesVariable() || Capture.getCaptureKind() != LCK_ByRef)
continue;

const ValueDecl* CapturedVarDecl = Capture.getCapturedVar();
if (CapturedVarDecl->getType().getCanonicalType()->isLValueReferenceType() ||
CapturedVarDecl->getType().getCanonicalType()->isRValueReferenceType()) {
continue;
}

if (CapturedVarDecl->getLocation() >= TasksLocation) {
diag(Capture.getLocation(), "captured here");
diag(CapturedVarDecl->getLocation(), "variable can be used after free");
diag(TasksLocation, "std::vector<Task> can die after variable");
}
}
}

} // namespace clang::tidy::bugprone
33 changes: 33 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/TaxiAsyncUseAfterFreeCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===--- TaxiAsyncUseAfterFreeCheck.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_TAXIASYNCUSEAFTERFREECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAXIASYNCUSEAFTERFREECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::bugprone {

/// Use-after-free for engine::Async.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/taxi-async-use-after-free.html
class TaxiAsyncUseAfterFreeCheck : public ClangTidyCheck {
public:
TaxiAsyncUseAfterFreeCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
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.CPlusPlus;
}
};

} // namespace clang::tidy::bugprone

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAXIASYNCUSEAFTERFREECHECK_H
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ New checks
pointer and store it as class members without handle the copy and move
constructors and the assignments.

- New :doc:`bugprone-taxi-async-use-after-free
<clang-tidy/checks/bugprone/taxi-async-use-after-free>` check.

Use-after-free for engine::Async.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make it more human-readable.


- New :doc:`bugprone-unintended-char-ostream-output
<clang-tidy/checks/bugprone/unintended-char-ostream-output>` check.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. title:: clang-tidy - bugprone-taxi-async-use-after-free

bugprone-taxi-async-use-after-free
==================================

FIXME: Describe what patterns does the check detect and why. Give examples.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing documentation.

1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ Clang-Tidy Checks
:doc:`bugprone-swapped-arguments <bugprone/swapped-arguments>`, "Yes"
:doc:`bugprone-switch-missing-default-case <bugprone/switch-missing-default-case>`,
:doc:`bugprone-tagged-union-member-count <bugprone/tagged-union-member-count>`,
:doc:`bugprone-taxi-async-use-after-free <bugprone/taxi-async-use-after-free>`, "Yes"
:doc:`bugprone-terminating-continue <bugprone/terminating-continue>`, "Yes"
:doc:`bugprone-throw-keyword-missing <bugprone/throw-keyword-missing>`,
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// RUN: %check_clang_tidy %s bugprone-taxi-async-use-after-free %t

namespace std {

template<typename T>
class vector {
public:
void push_back(T) {}
};

}

namespace engine {

template <typename Function, typename... Args>
int Async(Function&& f, Args&&... args) {
return 1;
}

}

void f_ok() {
int x = 1;
std::vector<int> v;

v.push_back(engine::Async([&x]{ x = 2;}));
}

void f_use_after_free() {
std::vector<int> v;
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: std::vector<Task> can die after variable [bugprone-taxi-async-use-after-free]
int x = 1;
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable can be used after free [bugprone-taxi-async-use-after-free]

v.push_back(engine::Async([&x]{ x = 2;}));
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: captured here [bugprone-taxi-async-use-after-free]
}

void f_ref() {
int xx = 1;
std::vector<int> v;
int &x = xx;

v.push_back(engine::Async([&x]{ x = 2;}));
}

void f_ref_ref() {
int xx = 1;
std::vector<int> v;
int &&x = static_cast<int&&>(xx);

v.push_back(engine::Async([&x]{ x = 2;}));
}