android: add a close-on-exec check on pipe2()

On Android, pipe2() is better to set O_CLOEXEC flag to avoid file
descriptor leakage.

Patch by Jian Cai!

Differential Revision: https://reviews.llvm.org/D62049

llvm-svn: 362672
This commit is contained in:
George Burgess IV 2019-06-06 05:21:39 +00:00
parent 5e7ca755d8
commit 3da331b456
8 changed files with 165 additions and 0 deletions

View File

@ -20,6 +20,7 @@
#include "CloexecInotifyInitCheck.h" #include "CloexecInotifyInitCheck.h"
#include "CloexecMemfdCreateCheck.h" #include "CloexecMemfdCreateCheck.h"
#include "CloexecOpenCheck.h" #include "CloexecOpenCheck.h"
#include "CloexecPipe2Check.h"
#include "CloexecSocketCheck.h" #include "CloexecSocketCheck.h"
#include "ComparisonInTempFailureRetryCheck.h" #include "ComparisonInTempFailureRetryCheck.h"
@ -49,6 +50,7 @@ public:
CheckFactories.registerCheck<CloexecMemfdCreateCheck>( CheckFactories.registerCheck<CloexecMemfdCreateCheck>(
"android-cloexec-memfd-create"); "android-cloexec-memfd-create");
CheckFactories.registerCheck<CloexecOpenCheck>("android-cloexec-open"); CheckFactories.registerCheck<CloexecOpenCheck>("android-cloexec-open");
CheckFactories.registerCheck<CloexecPipe2Check>("android-cloexec-pipe2");
CheckFactories.registerCheck<CloexecSocketCheck>("android-cloexec-socket"); CheckFactories.registerCheck<CloexecSocketCheck>("android-cloexec-socket");
CheckFactories.registerCheck<ComparisonInTempFailureRetryCheck>( CheckFactories.registerCheck<ComparisonInTempFailureRetryCheck>(
"android-comparison-in-temp-failure-retry"); "android-comparison-in-temp-failure-retry");

View File

@ -14,6 +14,7 @@ add_clang_library(clangTidyAndroidModule
CloexecInotifyInitCheck.cpp CloexecInotifyInitCheck.cpp
CloexecMemfdCreateCheck.cpp CloexecMemfdCreateCheck.cpp
CloexecOpenCheck.cpp CloexecOpenCheck.cpp
CloexecPipe2Check.cpp
CloexecSocketCheck.cpp CloexecSocketCheck.cpp
ComparisonInTempFailureRetryCheck.cpp ComparisonInTempFailureRetryCheck.cpp

View File

@ -0,0 +1,33 @@
//===--- CloexecPipe2Check.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 "CloexecPipe2Check.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace android {
void CloexecPipe2Check::registerMatchers(MatchFinder *Finder) {
registerMatchersImpl(Finder,
functionDecl(returns(isInteger()), hasName("pipe2"),
hasParameter(0, hasType(pointsTo(isInteger()))),
hasParameter(1, hasType(isInteger()))));
}
void CloexecPipe2Check::check(const MatchFinder::MatchResult &Result) {
insertMacroFlag(Result, /*MacroFlag=*/"O_CLOEXEC", /*ArgPos=*/1);
}
} // namespace android
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,34 @@
//===--- CloexecPipe2Check.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_ANDROID_CLOEXEC_PIPE2_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_PIPE2_H
#include "CloexecCheck.h"
namespace clang {
namespace tidy {
namespace android {
/// Finds code that uses pipe2() without using the O_CLOEXEC flag.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-pipe2.html
class CloexecPipe2Check : public CloexecCheck {
public:
CloexecPipe2Check(StringRef Name, ClangTidyContext *Context)
: CloexecCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace android
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_PIPE2_H

View File

@ -101,6 +101,11 @@ Improvements to clang-tidy
Finds and fixes ``absl::Time`` subtraction expressions to do subtraction Finds and fixes ``absl::Time`` subtraction expressions to do subtraction
in the Time domain instead of the numeric domain. in the Time domain instead of the numeric domain.
- New :doc:`android-cloexec-pipe2
<clang-tidy/checks/android-cloexec-pipe2>` check.
This checks ensures that ``pipe2()`` is called with the O_CLOEXEC flag.
- New :doc:`bugprone-unhandled-self-assignment - New :doc:`bugprone-unhandled-self-assignment
<clang-tidy/checks/bugprone-unhandled-self-assignment>` check. <clang-tidy/checks/bugprone-unhandled-self-assignment>` check.

View File

@ -0,0 +1,21 @@
.. title:: clang-tidy - android-cloexec-pipe2
android-cloexec-pipe2
=====================
This checks ensures that pipe2() is called with the O_CLOEXEC flag. The check also
adds the O_CLOEXEC flag that marks the file descriptor to be closed in child processes.
Without this flag a sensitive file descriptor can be leaked to a child process,
potentially into a lower-privileged SELinux domain.
Examples:
.. code-block:: c++
pipe2(pipefd, O_NONBLOCK);
Suggested replacement:
.. code-block:: c++
pipe2(pipefd, O_NONBLOCK | O_CLOEXEC);

View File

@ -32,6 +32,7 @@ Clang-Tidy Checks
android-cloexec-inotify-init1 android-cloexec-inotify-init1
android-cloexec-memfd-create android-cloexec-memfd-create
android-cloexec-open android-cloexec-open
android-cloexec-pipe2
android-cloexec-socket android-cloexec-socket
android-comparison-in-temp-failure-retry android-comparison-in-temp-failure-retry
boost-use-to-string boost-use-to-string

View File

@ -0,0 +1,68 @@
// RUN: %check_clang_tidy %s android-cloexec-pipe2 %t
#define O_NONBLOCK 1
#define __O_CLOEXEC 3
#define O_CLOEXEC __O_CLOEXEC
#define TEMP_FAILURE_RETRY(exp) \
({ \
int _rc; \
do { \
_rc = (exp); \
} while (_rc == -1); \
})
#define NULL 0
extern "C" int pipe2(int pipefd[2], int flags);
void warning() {
int pipefd[2];
pipe2(pipefd, O_NONBLOCK);
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: 'pipe2'
// CHECK-FIXES: pipe2(pipefd, O_NONBLOCK | O_CLOEXEC);
TEMP_FAILURE_RETRY(pipe2(pipefd, O_NONBLOCK));
// CHECK-MESSAGES: :[[@LINE-1]]:46: warning: 'pipe2'
// CHECK-FIXES: TEMP_FAILURE_RETRY(pipe2(pipefd, O_NONBLOCK | O_CLOEXEC));
}
void warningInMacroArugment() {
int pipefd[2];
pipe2(pipefd, 3);
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'pipe2'
// CHECK-FIXES: pipe2(pipefd, 3 | O_CLOEXEC);
TEMP_FAILURE_RETRY(pipe2(pipefd, 3));
// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 'pipe2'
// CHECK-FIXES: TEMP_FAILURE_RETRY(pipe2(pipefd, 3 | O_CLOEXEC));
int flag = O_NONBLOCK;
pipe2(pipefd, flag);
TEMP_FAILURE_RETRY(pipe2(pipefd, flag));
}
namespace i {
int pipe2(int pipefd[2], int flags);
void noWarning() {
int pipefd[2];
pipe2(pipefd, O_NONBLOCK);
TEMP_FAILURE_RETRY(pipe2(pipefd, O_NONBLOCK));
}
} // namespace i
void noWarning() {
int pipefd[2];
pipe2(pipefd, O_CLOEXEC);
TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
pipe2(pipefd, O_NONBLOCK | O_CLOEXEC);
TEMP_FAILURE_RETRY(pipe2(pipefd, O_NONBLOCK | O_CLOEXEC));
}
class G {
public:
int pipe2(int pipefd[2], int flags);
void noWarning() {
int pipefd[2];
pipe2(pipefd, O_NONBLOCK);
TEMP_FAILURE_RETRY(pipe2(pipefd, O_NONBLOCK));
}
};