diff --git a/clang-tools-extra/clang-tidy/android/AndroidTidyModule.cpp b/clang-tools-extra/clang-tidy/android/AndroidTidyModule.cpp --- a/clang-tools-extra/clang-tidy/android/AndroidTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/android/AndroidTidyModule.cpp @@ -24,6 +24,7 @@ #include "CloexecPipe2Check.h" #include "CloexecSocketCheck.h" #include "ComparisonInTempFailureRetryCheck.h" +#include "PosixReturnCheck.h" using namespace clang::ast_matchers; @@ -56,6 +57,8 @@ CheckFactories.registerCheck("android-cloexec-socket"); CheckFactories.registerCheck( "android-comparison-in-temp-failure-retry"); + CheckFactories.registerCheck( + "android-posix-return"); } }; diff --git a/clang-tools-extra/clang-tidy/android/CMakeLists.txt b/clang-tools-extra/clang-tidy/android/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/android/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/android/CMakeLists.txt @@ -18,6 +18,7 @@ CloexecPipe2Check.cpp CloexecSocketCheck.cpp ComparisonInTempFailureRetryCheck.cpp + PosixReturnCheck.cpp LINK_LIBS clangAST diff --git a/clang-tools-extra/clang-tidy/android/PosixReturnCheck.h b/clang-tools-extra/clang-tidy/android/PosixReturnCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/android/PosixReturnCheck.h @@ -0,0 +1,30 @@ +//===--- PosixReturnCheck.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_POSIX_RETURN_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_POSIX_RETURN_CHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace android { + +class PosixReturnCheck: public ClangTidyCheck { +public: + PosixReturnCheck(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_POSIX_RETURN_CHECK_H diff --git a/clang-tools-extra/clang-tidy/android/PosixReturnCheck.cpp b/clang-tools-extra/clang-tidy/android/PosixReturnCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/android/PosixReturnCheck.cpp @@ -0,0 +1,42 @@ +//===--- PosixReturnCheck.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 "../utils/Matchers.h" +#include "PosixReturnCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace android { + +void PosixReturnCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + binaryOperator( + hasOperatorName("<"), + hasLHS(callExpr(callee(functionDecl(matchesName("^::posix_"), unless(hasName("posix_openpt")))))), + hasRHS(integerLiteral(equals(0)))).bind("binop"), + this); + Finder->addMatcher( + binaryOperator( + hasOperatorName("=="), + hasLHS(callExpr(callee(functionDecl(matchesName("^::posix_"), unless(hasName("posix_openpt")))))), + hasRHS(unaryOperator(hasOperatorName("-"), hasUnaryOperand(integerLiteral())))).bind("binop"), + this); +} + +void PosixReturnCheck::check(const MatchFinder::MatchResult &Result) { + const auto *BinOp = Result.Nodes.getNodeAs("binop"); + diag(BinOp->getOperatorLoc(), "posix functions (except posix_openpt) never return negative values"); +} + +} // namespace android +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -111,6 +111,12 @@ This checks ensures that ``pipe2()`` is called with the O_CLOEXEC flag. +- New :doc:`android-posix-return + ` check. + + Checks if any calls to POSIX functions (except ``posix_openpt``) expect negative + return values + - New :doc:`bugprone-unhandled-self-assignment ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/android-posix-return.rst b/clang-tools-extra/docs/clang-tidy/checks/android-posix-return.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/android-posix-return.rst @@ -0,0 +1,21 @@ +.. title:: clang-tidy - android-posix-return + +android-posix-return +==================== + +Checks if any calls to POSIX functions (except ``posix_openpt``) expect negative +return values. These functions return either ``0`` on success or an ``errno`` on failure, +which is positive only. + +Example buggy usage looks like: + +.. code-block:: c + + if (posix_fadvise(...) < 0) { + +This will never happen as the return value is always non-negative. A simple fix could be: + +.. code-block:: c + + int ret = posix_fadvise(...); + if (ret != 0) ... diff --git a/clang-tools-extra/test/clang-tidy/android-posix-return.cpp b/clang-tools-extra/test/clang-tidy/android-posix-return.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/android-posix-return.cpp @@ -0,0 +1,89 @@ +// RUN: %check_clang_tidy %s android-posix-return %t + +#define NULL nullptr +#define ZERO 0 +#define NEGATIVE_ONE -1 + +typedef long off_t; +typedef int size_t; +typedef int pid_t; +typedef struct __posix_spawn_file_actions* posix_spawn_file_actions_t; +typedef struct __posix_spawnattr* posix_spawnattr_t; + +extern "C" int posix_fadvise(int fd, off_t offset, off_t len, int advice); +extern "C" int posix_fallocate(int fd, off_t offset, off_t len); +extern "C" int posix_madvise(void *addr, size_t len, int advice); +extern "C" int posix_memalign(void **memptr, size_t alignment, size_t size); +extern "C" int posix_openpt(int flags); +extern "C" int posix_spawn(pid_t *pid, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]); +extern "C" int posix_spawnp(pid_t *pid, const char *file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[]); + +void warningLtZero() { + if (posix_fadvise(0, 0, 0, 0) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: + if (posix_fallocate(0, 0, 0) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: + if (posix_madvise(NULL, 0, 0) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: + if (posix_memalign(NULL, 0, 0) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: + if (posix_spawn(NULL, NULL, NULL, NULL, {NULL}, {NULL}) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:59: warning: + if (posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:60: warning: +} + +void warningEqualsNegative() { + if (posix_fadvise(0, 0, 0, 0) == -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: + if (posix_fallocate(0, 0, 0) == -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: + if (posix_madvise(NULL, 0, 0) == -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: + if (posix_memalign(NULL, 0, 0) == -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: + if (posix_spawn(NULL, NULL, NULL, NULL, {NULL}, {NULL}) == -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:59: warning: + if (posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) == -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:60: warning: +} + +void WarningWithMacro() { + if (posix_fadvise(0, 0, 0, 0) < ZERO) {} + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: + if (posix_fadvise(0, 0, 0, 0) == NEGATIVE_ONE) {} + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: +} + +void noWarning() { + if (posix_openpt(0) < 0) {} + if (posix_openpt(0) == -1) {} + if (posix_fadvise(0, 0, 0, 0) >= 0) {} + if (posix_fadvise(0, 0, 0, 0) == 1) {} +} + +namespace i { +int posix_fadvise(int fd, off_t offset, off_t len, int advice); + +void noWarning() { + if (posix_fadvise(0, 0, 0, 0) < 0) {} + if (posix_fadvise(0, 0, 0, 0) == -1) {} +} + +} // namespace i + +class G { + public: + int posix_fadvise(int fd, off_t offset, off_t len, int advice); + + void noWarning() { + if (posix_fadvise(0, 0, 0, 0) < 0) {} + if (posix_fadvise(0, 0, 0, 0) == -1) {} + } +};