Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[clang-tidy] Add a new Android check "android-cloexec-socket"
Summary: socket() is better to include SOCK_CLOEXEC in its type argument to avoid the file descriptor leakage. Reviewers: chh, Eugene.Zelenko, alexfh, hokein, aaron.ballman Reviewed By: chh, alexfh Subscribers: srhines, mgorny, JDevlieghere, xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D34913 llvm-svn: 307818
- Loading branch information
1 parent
4fc6966
commit b38045d
Showing
11 changed files
with
233 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
clang-tools-extra/clang-tidy/android/CloexecSocketCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
//===--- CloexecSocketCheck.cpp - clang-tidy-------------------------------===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "CloexecSocketCheck.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 { | ||
|
||
static constexpr const char *SOCK_CLOEXEC = "SOCK_CLOEXEC"; | ||
|
||
void CloexecSocketCheck::registerMatchers(MatchFinder *Finder) { | ||
Finder->addMatcher( | ||
callExpr(callee(functionDecl(isExternC(), returns(isInteger()), | ||
hasName("socket"), | ||
hasParameter(0, hasType(isInteger())), | ||
hasParameter(1, hasType(isInteger())), | ||
hasParameter(2, hasType(isInteger()))) | ||
.bind("funcDecl"))) | ||
.bind("socketFn"), | ||
this); | ||
} | ||
|
||
void CloexecSocketCheck::check(const MatchFinder::MatchResult &Result) { | ||
const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("socketFn"); | ||
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl"); | ||
const Expr *FlagArg = MatchedCall->getArg(1); | ||
SourceManager &SM = *Result.SourceManager; | ||
|
||
if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM, | ||
Result.Context->getLangOpts(), SOCK_CLOEXEC)) | ||
return; | ||
|
||
SourceLocation EndLoc = | ||
Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM, | ||
Result.Context->getLangOpts()); | ||
|
||
diag(EndLoc, "%0 should use %1 where possible") | ||
<< FD << SOCK_CLOEXEC | ||
<< FixItHint::CreateInsertion(EndLoc, | ||
(Twine(" | ") + SOCK_CLOEXEC).str()); | ||
} | ||
|
||
} // namespace android | ||
} // namespace tidy | ||
} // namespace clang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
//===--- CloexecSocketCheck.h - clang-tidy-----------------------*- C++ -*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H | ||
|
||
#include "../ClangTidy.h" | ||
|
||
namespace clang { | ||
namespace tidy { | ||
namespace android { | ||
|
||
/// Finds code that uses socket() without using the SOCK_CLOEXEC flag. | ||
/// | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-socket.html | ||
class CloexecSocketCheck : public ClangTidyCheck { | ||
public: | ||
CloexecSocketCheck(StringRef Name, ClangTidyContext *Context) | ||
: ClangTidyCheck(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_SOCKET_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
clang-tools-extra/docs/clang-tidy/checks/android-cloexec-socket.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
.. title:: clang-tidy - android-cloexec-socket | ||
|
||
android-cloexec-socket | ||
====================== | ||
|
||
``socket()`` should include ``SOCK_CLOEXEC`` in its type argument to avoid the | ||
file descriptor leakage. Without this flag, an opened sensitive file would | ||
remain open across a fork+exec to a lower-privileged SELinux domain. | ||
|
||
Examples: | ||
|
||
.. code-block:: c++ | ||
|
||
socket(domain, type, SOCK_STREAM); | ||
|
||
// becomes | ||
|
||
socket(domain, type, SOCK_STREAM | SOCK_CLOEXEC); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
clang-tools-extra/test/clang-tidy/android-cloexec-socket.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// RUN: %check_clang_tidy %s android-cloexec-socket %t | ||
|
||
#define SOCK_STREAM 1 | ||
#define SOCK_DGRAM 2 | ||
#define __O_CLOEXEC 3 | ||
#define SOCK_CLOEXEC __O_CLOEXEC | ||
#define TEMP_FAILURE_RETRY(exp) \ | ||
({ \ | ||
int _rc; \ | ||
do { \ | ||
_rc = (exp); \ | ||
} while (_rc == -1); \ | ||
}) | ||
|
||
extern "C" int socket(int domain, int type, int protocol); | ||
|
||
void a() { | ||
socket(0, SOCK_STREAM, 0); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'socket' should use SOCK_CLOEXEC where possible [android-cloexec-socket] | ||
// CHECK-FIXES: socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0) | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0)); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:43: warning: 'socket' | ||
// CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0)) | ||
socket(0, SOCK_STREAM | SOCK_DGRAM, 0); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 'socket' | ||
// CHECK-FIXES: socket(0, SOCK_STREAM | SOCK_DGRAM | SOCK_CLOEXEC, 0) | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0)); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:56: warning: 'socket' | ||
// CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM | SOCK_CLOEXEC, 0)) | ||
} | ||
|
||
void f() { | ||
socket(0, 3, 0); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'socket' | ||
// CHECK-FIXES: socket(0, 3 | SOCK_CLOEXEC, 0) | ||
TEMP_FAILURE_RETRY(socket(0, 3, 0)); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: 'socket' | ||
// CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, 3 | SOCK_CLOEXEC, 0)) | ||
|
||
int flag = 3; | ||
socket(0, flag, 0); | ||
TEMP_FAILURE_RETRY(socket(0, flag, 0)); | ||
} | ||
|
||
namespace i { | ||
int socket(int domain, int type, int protocol); | ||
|
||
void d() { | ||
socket(0, SOCK_STREAM, 0); | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0)); | ||
socket(0, SOCK_STREAM | SOCK_DGRAM, 0); | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0)); | ||
} | ||
|
||
} // namespace i | ||
|
||
void e() { | ||
socket(0, SOCK_CLOEXEC, 0); | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_CLOEXEC, 0)); | ||
socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0); | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0)); | ||
socket(0, SOCK_STREAM | SOCK_CLOEXEC | SOCK_DGRAM, 0); | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC | SOCK_DGRAM, 0)); | ||
} | ||
|
||
class G { | ||
public: | ||
int socket(int domain, int type, int protocol); | ||
void d() { | ||
socket(0, SOCK_STREAM, 0); | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0)); | ||
socket(0, SOCK_STREAM | SOCK_DGRAM, 0); | ||
TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0)); | ||
} | ||
}; |