[clang] 9d48705 - [clang][analyzer] Teach the BlockInCriticalSectionChecker about O_NONBLOCK streams (#127049)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 17 06:35:43 PST 2025
Author: flovent
Date: 2025-02-17T15:35:40+01:00
New Revision: 9d487050a144b895950a6fd48b993513a714e69d
URL: https://github.com/llvm/llvm-project/commit/9d487050a144b895950a6fd48b993513a714e69d
DIFF: https://github.com/llvm/llvm-project/commit/9d487050a144b895950a6fd48b993513a714e69d.diff
LOG: [clang][analyzer] Teach the BlockInCriticalSectionChecker about O_NONBLOCK streams (#127049)
this PR close #124474
when calling `read` and `recv` function for a non-block file descriptor
or a invalid file descriptor(`-1`), it will not cause block inside a
critical section.
this commit checks for non-block file descriptor assigned by `open`
function with `O_NONBLOCK` flag.
---------
Co-authored-by: Balazs Benics <benicsbalazs at gmail.com>
Added:
clang/test/Analysis/Inputs/system-header-simulator-cxx-std-locks.h
clang/test/Analysis/issue-124474.cpp
Modified:
clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 7460781799d08..bf35bee70870b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -145,6 +145,57 @@ using MutexDescriptor =
std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
RAIIMutexDescriptor>;
+class SuppressNonBlockingStreams : public BugReporterVisitor {
+private:
+ const CallDescription OpenFunction{CDM::CLibrary, {"open"}, 2};
+ SymbolRef StreamSym;
+ const int NonBlockMacroVal;
+ bool Satisfied = false;
+
+public:
+ SuppressNonBlockingStreams(SymbolRef StreamSym, int NonBlockMacroVal)
+ : StreamSym(StreamSym), NonBlockMacroVal(NonBlockMacroVal) {}
+
+ static void *getTag() {
+ static bool Tag;
+ return &Tag;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(getTag());
+ }
+
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override {
+ if (Satisfied)
+ return nullptr;
+
+ std::optional<StmtPoint> Point = N->getLocationAs<StmtPoint>();
+ if (!Point)
+ return nullptr;
+
+ const auto *CE = Point->getStmtAs<CallExpr>();
+ if (!CE || !OpenFunction.matchesAsWritten(*CE))
+ return nullptr;
+
+ if (N->getSVal(CE).getAsSymbol() != StreamSym)
+ return nullptr;
+
+ Satisfied = true;
+
+ // Check if open's second argument contains O_NONBLOCK
+ const llvm::APSInt *FlagVal = N->getSVal(CE->getArg(1)).getAsInteger();
+ if (!FlagVal)
+ return nullptr;
+
+ if ((*FlagVal & NonBlockMacroVal) != 0)
+ BR.markInvalid(getTag(), nullptr);
+
+ return nullptr;
+ }
+};
+
class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
private:
const std::array<MutexDescriptor, 8> MutexDescriptors{
@@ -182,6 +233,9 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
const BugType BlockInCritSectionBugType{
this, "Call to blocking function in critical section", "Blocking Error"};
+ using O_NONBLOCKValueTy = std::optional<int>;
+ mutable std::optional<O_NONBLOCKValueTy> O_NONBLOCKValue;
+
void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
[[nodiscard]] const NoteTag *createCritSectionNote(CritSectionMarker M,
@@ -337,6 +391,28 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
<< "' inside of critical section";
auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
os.str(), ErrNode);
+ // for 'read' and 'recv' call, check whether it's file descriptor(first
+ // argument) is
+ // created by 'open' API with O_NONBLOCK flag or is equal to -1, they will
+ // not cause block in these situations, don't report
+ StringRef FuncName = Call.getCalleeIdentifier()->getName();
+ if (FuncName == "read" || FuncName == "recv") {
+ SVal SV = Call.getArgSVal(0);
+ SValBuilder &SVB = C.getSValBuilder();
+ ProgramStateRef state = C.getState();
+ ConditionTruthVal CTV =
+ state->areEqual(SV, SVB.makeIntVal(-1, C.getASTContext().IntTy));
+ if (CTV.isConstrainedTrue())
+ return;
+
+ if (SymbolRef SR = SV.getAsSymbol()) {
+ if (!O_NONBLOCKValue)
+ O_NONBLOCKValue = tryExpandAsInteger(
+ "O_NONBLOCK", C.getBugReporter().getPreprocessor());
+ if (*O_NONBLOCKValue)
+ R->addVisitor<SuppressNonBlockingStreams>(SR, **O_NONBLOCKValue);
+ }
+ }
R->addRange(Call.getSourceRange());
R->markInteresting(Call.getReturnValue());
C.emitReport(std::move(R));
diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-locks.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-locks.h
new file mode 100644
index 0000000000000..054dd5405e1be
--- /dev/null
+++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-locks.h
@@ -0,0 +1,13 @@
+#pragma clang system_header
+
+namespace std {
+struct mutex {
+ void lock();
+ void unlock();
+};
+
+template <typename T> struct lock_guard {
+ lock_guard(std::mutex &);
+ ~lock_guard();
+};
+} // namespace std
diff --git a/clang/test/Analysis/issue-124474.cpp b/clang/test/Analysis/issue-124474.cpp
new file mode 100644
index 0000000000000..ae30c4db552c1
--- /dev/null
+++ b/clang/test/Analysis/issue-124474.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_analyze_cc1 \
+// RUN: -analyzer-checker=core,unix.BlockInCriticalSection \
+// RUN: -analyzer-output text -verify %s
+
+// expected-no-diagnostics
+
+#include "Inputs/system-header-simulator-cxx-std-locks.h"
+
+std::mutex mtx;
+using ssize_t = long long;
+using size_t = unsigned long long;
+int open(const char *__file, int __oflag, ...);
+ssize_t read(int fd, void *buf, size_t count);
+void close(int fd);
+#define O_RDONLY 00
+#define O_NONBLOCK 04000
+
+void foo() {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ const char *filename = "example.txt";
+ int fd = open(filename, O_RDONLY | O_NONBLOCK);
+
+ char buffer[200] = {};
+ read(fd, buffer, 199); // no-warning: fd is a non-block file descriptor or equals to -1
+ close(fd);
+}
+
+void foo1(int fd) {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ const char *filename = "example.txt";
+ char buffer[200] = {};
+ if (fd == -1)
+ read(fd, buffer, 199); // no-warning: consider file descriptor is a symbol equals to -1
+ close(fd);
+}
More information about the cfe-commits
mailing list