[clang] [clang][analyzer] Fix false positive of BlockInCriticalSectionChecker (PR #127049)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 14 05:47:40 PST 2025
https://github.com/flovent updated https://github.com/llvm/llvm-project/pull/127049
>From c916dadbaf6021eda606d76784115698a9800571 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Thu, 13 Feb 2025 20:17:20 +0800
Subject: [PATCH 1/4] [clang][analyzer] fix false positive of
BlockInCriticalSectionChecker
---
.../BlockInCriticalSectionChecker.cpp | 67 ++++++++++++++++++-
clang/test/Analysis/issue-124474.cpp | 49 ++++++++++++++
2 files changed, 115 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Analysis/issue-124474.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 7460781799d08..db784f2cc77b2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -145,7 +145,8 @@ using MutexDescriptor =
std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
RAIIMutexDescriptor>;
-class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
+class BlockInCriticalSectionChecker
+ : public Checker<check::PostCall, check::DeadSymbols> {
private:
const std::array<MutexDescriptor, 8> MutexDescriptors{
// NOTE: There are standard library implementations where some methods
@@ -179,6 +180,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
{CDM::CLibrary, {"read"}},
{CDM::CLibrary, {"recv"}}};
+ const CallDescription OpenFunction{CDM::CLibrary, {"open"}, 2};
+
const BugType BlockInCritSectionBugType{
this, "Call to blocking function in critical section", "Blocking Error"};
@@ -197,6 +200,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
CheckerContext &C) const;
+ void handleOpen(const CallEvent &Call, CheckerContext &C) const;
+
[[nodiscard]] bool isBlockingInCritSection(const CallEvent &Call,
CheckerContext &C) const;
@@ -205,11 +210,14 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
/// Process lock.
/// Process blocking functions (sleep, getc, fgets, read, recv)
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
};
} // end anonymous namespace
REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
+REGISTER_SET_WITH_PROGRAMSTATE(NonBlockFileDescriptor, SymbolRef)
// Iterator traits for ImmutableList data structure
// that enable the use of STL algorithms.
@@ -306,6 +314,25 @@ void BlockInCriticalSectionChecker::handleUnlock(
C.addTransition(State);
}
+void BlockInCriticalSectionChecker::handleOpen(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *Flag = Call.getArgExpr(1);
+ static std::optional<int> ValueOfONonBlockVFlag =
+ tryExpandAsInteger("O_NONBLOCK", C.getBugReporter().getPreprocessor());
+ if (!ValueOfONonBlockVFlag)
+ return;
+
+ SVal FlagSV = C.getState()->getSVal(Flag, C.getLocationContext());
+ const llvm::APSInt *FlagV = FlagSV.getAsInteger();
+ if (!FlagV)
+ return;
+
+ if ((*FlagV & ValueOfONonBlockVFlag.value()) != 0)
+ if (SymbolRef SR = Call.getReturnValue().getAsSymbol()) {
+ C.addTransition(C.getState()->add<NonBlockFileDescriptor>(SR));
+ }
+}
+
bool BlockInCriticalSectionChecker::isBlockingInCritSection(
const CallEvent &Call, CheckerContext &C) const {
return BlockingFunctions.contains(Call) &&
@@ -315,6 +342,27 @@ bool BlockInCriticalSectionChecker::isBlockingInCritSection(
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
if (isBlockingInCritSection(Call, C)) {
+ // 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") {
+ const auto *Arg = Call.getArgExpr(0);
+ if (!Arg)
+ return;
+
+ SVal SV = C.getSVal(Arg);
+ if (const auto *IntValue = SV.getAsInteger()) {
+ if (*IntValue == -1)
+ return;
+ }
+
+ SymbolRef SR = C.getSVal(Arg).getAsSymbol();
+ if (SR && C.getState()->contains<NonBlockFileDescriptor>(SR)) {
+ return;
+ }
+ }
reportBlockInCritSection(Call, C);
} else if (std::optional<MutexDescriptor> LockDesc =
checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
@@ -322,9 +370,26 @@ void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
} else if (std::optional<MutexDescriptor> UnlockDesc =
checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
handleUnlock(*UnlockDesc, Call, C);
+ } else if (OpenFunction.matches(Call)) {
+ handleOpen(Call, C);
}
}
+void BlockInCriticalSectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Remove the dead symbols from the NonBlockFileDescriptor set.
+ NonBlockFileDescriptorTy Tracked = State->get<NonBlockFileDescriptor>();
+ for (SymbolRef SR : Tracked) {
+ if (SymReaper.isDead(SR)) {
+ State = State->remove<NonBlockFileDescriptor>(SR);
+ }
+ }
+
+ C.addTransition(State);
+}
+
void BlockInCriticalSectionChecker::reportBlockInCritSection(
const CallEvent &Call, CheckerContext &C) const {
ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
diff --git a/clang/test/Analysis/issue-124474.cpp b/clang/test/Analysis/issue-124474.cpp
new file mode 100644
index 0000000000000..09e3d4f3f9ad9
--- /dev/null
+++ b/clang/test/Analysis/issue-124474.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_analyze_cc1 \
+// RUN: -analyzer-checker=unix.BlockInCriticalSection \
+// RUN: -std=c++11 \
+// RUN: -analyzer-output text \
+// RUN: -verify %s
+
+// expected-no-diagnostics
+
+namespace std {
+ struct mutex {
+ void lock() {}
+ void unlock() {}
+ };
+ template<typename T>
+ struct lock_guard {
+ lock_guard<T>(std::mutex) {}
+ ~lock_guard<T>() {}
+ };
+ template<typename T>
+ struct unique_lock {
+ unique_lock<T>(std::mutex) {}
+ ~unique_lock<T>() {}
+ };
+ template<typename T>
+ struct not_real_lock {
+ not_real_lock<T>(std::mutex) {}
+ };
+ } // namespace std
+
+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
+ close(fd);
+}
>From f2f928798e715ccd2739bfa44d3d09fffce25b16 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Thu, 13 Feb 2025 20:32:53 +0800
Subject: [PATCH 2/4] add new line
---
clang/test/Analysis/issue-124474.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/test/Analysis/issue-124474.cpp b/clang/test/Analysis/issue-124474.cpp
index 09e3d4f3f9ad9..7136bbb316c67 100644
--- a/clang/test/Analysis/issue-124474.cpp
+++ b/clang/test/Analysis/issue-124474.cpp
@@ -47,3 +47,4 @@ void foo()
read(fd, buffer, 199); // no-warning: fd is a non-block file descriptor
close(fd);
}
+
>From 20bec01386e957146250819540a97bedaa97066d Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 14 Feb 2025 21:05:38 +0800
Subject: [PATCH 3/4] update based on review advice
---
.../BlockInCriticalSectionChecker.cpp | 32 ++++++------
.../system-header-simulator-cxx-std-locks.h | 17 ++++++
clang/test/Analysis/issue-124474.cpp | 52 ++++++++-----------
3 files changed, 54 insertions(+), 47 deletions(-)
create mode 100644 clang/test/Analysis/Inputs/system-header-simulator-cxx-std-locks.h
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index db784f2cc77b2..4a59c9f39eb7b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -185,6 +185,9 @@ class BlockInCriticalSectionChecker
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,
@@ -317,17 +320,16 @@ void BlockInCriticalSectionChecker::handleUnlock(
void BlockInCriticalSectionChecker::handleOpen(const CallEvent &Call,
CheckerContext &C) const {
const auto *Flag = Call.getArgExpr(1);
- static std::optional<int> ValueOfONonBlockVFlag =
- tryExpandAsInteger("O_NONBLOCK", C.getBugReporter().getPreprocessor());
- if (!ValueOfONonBlockVFlag)
- return;
-
SVal FlagSV = C.getState()->getSVal(Flag, C.getLocationContext());
const llvm::APSInt *FlagV = FlagSV.getAsInteger();
if (!FlagV)
return;
- if ((*FlagV & ValueOfONonBlockVFlag.value()) != 0)
+ if (!O_NONBLOCKValue)
+ O_NONBLOCKValue =
+ tryExpandAsInteger("O_NONBLOCK", C.getBugReporter().getPreprocessor());
+
+ if (*O_NONBLOCKValue && ((*FlagV & **O_NONBLOCKValue) != 0))
if (SymbolRef SR = Call.getReturnValue().getAsSymbol()) {
C.addTransition(C.getState()->add<NonBlockFileDescriptor>(SR));
}
@@ -348,18 +350,16 @@ void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
// not cause block in these situations, don't report
StringRef FuncName = Call.getCalleeIdentifier()->getName();
if (FuncName == "read" || FuncName == "recv") {
- const auto *Arg = Call.getArgExpr(0);
- if (!Arg)
+ 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;
- SVal SV = C.getSVal(Arg);
- if (const auto *IntValue = SV.getAsInteger()) {
- if (*IntValue == -1)
- return;
- }
-
- SymbolRef SR = C.getSVal(Arg).getAsSymbol();
- if (SR && C.getState()->contains<NonBlockFileDescriptor>(SR)) {
+ SymbolRef SR = SV.getAsSymbol();
+ if (SR && state->contains<NonBlockFileDescriptor>(SR)) {
return;
}
}
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..91e7c9f63fb55
--- /dev/null
+++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-locks.h
@@ -0,0 +1,17 @@
+// This is a fake system header with divide-by-zero bugs introduced in
+// c++ std library functions. We use these bugs to test hard-coded
+// suppression of diagnostics within standard library functions that are known
+// to produce false positives.
+
+#pragma clang system_header
+namespace std {
+struct mutex {
+ void lock() {}
+ void unlock() {}
+};
+
+template <typename T> struct lock_guard {
+ lock_guard<T>(std::mutex) {}
+ ~lock_guard<T>() {}
+};
+} // namespace std
\ No newline at end of file
diff --git a/clang/test/Analysis/issue-124474.cpp b/clang/test/Analysis/issue-124474.cpp
index 7136bbb316c67..d4861691a13ac 100644
--- a/clang/test/Analysis/issue-124474.cpp
+++ b/clang/test/Analysis/issue-124474.cpp
@@ -6,45 +6,35 @@
// expected-no-diagnostics
-namespace std {
- struct mutex {
- void lock() {}
- void unlock() {}
- };
- template<typename T>
- struct lock_guard {
- lock_guard<T>(std::mutex) {}
- ~lock_guard<T>() {}
- };
- template<typename T>
- struct unique_lock {
- unique_lock<T>(std::mutex) {}
- ~unique_lock<T>() {}
- };
- template<typename T>
- struct not_real_lock {
- not_real_lock<T>(std::mutex) {}
- };
- } // namespace std
+#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, ...);
+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
+#define O_RDONLY 00
+#define O_NONBLOCK 04000
-void foo()
-{
- std::lock_guard<std::mutex> lock(mtx);
+void foo() {
+ std::lock_guard<std::mutex> lock(mtx);
- const char *filename = "example.txt";
- int fd = open(filename, O_RDONLY | O_NONBLOCK);
+ 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
- close(fd);
+ 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);
+}
+
\ No newline at end of file
>From edac8dc8065dd0e026873440b9939e4c004b0909 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 14 Feb 2025 21:47:25 +0800
Subject: [PATCH 4/4] use BugReportVisitor instead set
---
.../BlockInCriticalSectionChecker.cpp | 142 ++++++++++--------
1 file changed, 79 insertions(+), 63 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 4a59c9f39eb7b..7602813f8bd5d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -145,8 +145,63 @@ using MutexDescriptor =
std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
RAIIMutexDescriptor>;
-class BlockInCriticalSectionChecker
- : public Checker<check::PostCall, check::DeadSymbols> {
+class NonBlockOpenVisitor : public BugReporterVisitor {
+private:
+ SymbolRef SR;
+ int O_NONBLOCKValue;
+ const CallDescription OpenFunction;
+ bool Satisfied;
+
+public:
+ NonBlockOpenVisitor(SymbolRef SR, int O_NONBLOCKValue)
+ : SR(SR), O_NONBLOCKValue(O_NONBLOCKValue),
+ OpenFunction(CDM::CLibrary, {"open"}, 2), Satisfied(false) {}
+
+ static void *getTag() {
+ static int Tag = 0;
+ return static_cast<void *>(&Tag);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(getTag());
+ ID.AddPointer(SR);
+ }
+
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override {
+ if (Satisfied)
+ return nullptr;
+
+ std::optional<StmtPoint> Point = N->getLocation().getAs<StmtPoint>();
+ if (!Point)
+ return nullptr;
+
+ auto *CE = dyn_cast<CallExpr>(Point->getStmt());
+ if (!CE || !OpenFunction.matchesAsWritten(*CE))
+ return nullptr;
+
+ SVal SV = N->getSVal(CE);
+ if (SV.getAsSymbol() != SR)
+ return nullptr;
+
+ Satisfied = true;
+
+ // Check if open's second argument contains O_NONBLOCK
+ const auto *Flag = CE->getArg(1);
+ SVal FlagSV = N->getSVal(Flag);
+ const llvm::APSInt *FlagV = FlagSV.getAsInteger();
+ if (!FlagV)
+ return nullptr;
+
+ if ((*FlagV & O_NONBLOCKValue) != 0)
+ BR.markInvalid(getTag(), nullptr);
+
+ return nullptr;
+ }
+};
+
+class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
private:
const std::array<MutexDescriptor, 8> MutexDescriptors{
// NOTE: There are standard library implementations where some methods
@@ -180,8 +235,6 @@ class BlockInCriticalSectionChecker
{CDM::CLibrary, {"read"}},
{CDM::CLibrary, {"recv"}}};
- const CallDescription OpenFunction{CDM::CLibrary, {"open"}, 2};
-
const BugType BlockInCritSectionBugType{
this, "Call to blocking function in critical section", "Blocking Error"};
@@ -203,8 +256,6 @@ class BlockInCriticalSectionChecker
void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
CheckerContext &C) const;
- void handleOpen(const CallEvent &Call, CheckerContext &C) const;
-
[[nodiscard]] bool isBlockingInCritSection(const CallEvent &Call,
CheckerContext &C) const;
@@ -213,14 +264,11 @@ class BlockInCriticalSectionChecker
/// Process lock.
/// Process blocking functions (sleep, getc, fgets, read, recv)
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-
- void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
};
} // end anonymous namespace
REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
-REGISTER_SET_WITH_PROGRAMSTATE(NonBlockFileDescriptor, SymbolRef)
// Iterator traits for ImmutableList data structure
// that enable the use of STL algorithms.
@@ -317,24 +365,6 @@ void BlockInCriticalSectionChecker::handleUnlock(
C.addTransition(State);
}
-void BlockInCriticalSectionChecker::handleOpen(const CallEvent &Call,
- CheckerContext &C) const {
- const auto *Flag = Call.getArgExpr(1);
- SVal FlagSV = C.getState()->getSVal(Flag, C.getLocationContext());
- const llvm::APSInt *FlagV = FlagSV.getAsInteger();
- if (!FlagV)
- return;
-
- if (!O_NONBLOCKValue)
- O_NONBLOCKValue =
- tryExpandAsInteger("O_NONBLOCK", C.getBugReporter().getPreprocessor());
-
- if (*O_NONBLOCKValue && ((*FlagV & **O_NONBLOCKValue) != 0))
- if (SymbolRef SR = Call.getReturnValue().getAsSymbol()) {
- C.addTransition(C.getState()->add<NonBlockFileDescriptor>(SR));
- }
-}
-
bool BlockInCriticalSectionChecker::isBlockingInCritSection(
const CallEvent &Call, CheckerContext &C) const {
return BlockingFunctions.contains(Call) &&
@@ -344,25 +374,6 @@ bool BlockInCriticalSectionChecker::isBlockingInCritSection(
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
if (isBlockingInCritSection(Call, C)) {
- // 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;
-
- SymbolRef SR = SV.getAsSymbol();
- if (SR && state->contains<NonBlockFileDescriptor>(SR)) {
- return;
- }
- }
reportBlockInCritSection(Call, C);
} else if (std::optional<MutexDescriptor> LockDesc =
checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
@@ -370,26 +381,9 @@ void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
} else if (std::optional<MutexDescriptor> UnlockDesc =
checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
handleUnlock(*UnlockDesc, Call, C);
- } else if (OpenFunction.matches(Call)) {
- handleOpen(Call, C);
}
}
-void BlockInCriticalSectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
-
- // Remove the dead symbols from the NonBlockFileDescriptor set.
- NonBlockFileDescriptorTy Tracked = State->get<NonBlockFileDescriptor>();
- for (SymbolRef SR : Tracked) {
- if (SymReaper.isDead(SR)) {
- State = State->remove<NonBlockFileDescriptor>(SR);
- }
- }
-
- C.addTransition(State);
-}
-
void BlockInCriticalSectionChecker::reportBlockInCritSection(
const CallEvent &Call, CheckerContext &C) const {
ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
@@ -402,6 +396,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<NonBlockOpenVisitor>(SR, **O_NONBLOCKValue);
+ }
+ }
R->addRange(Call.getSourceRange());
R->markInteresting(Call.getReturnValue());
C.emitReport(std::move(R));
More information about the cfe-commits
mailing list