@@ -145,7 +145,8 @@ using MutexDescriptor =
145145 std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
146146 RAIIMutexDescriptor>;
147147
148- class BlockInCriticalSectionChecker : public Checker <check::PostCall> {
148+ class BlockInCriticalSectionChecker
149+ : public Checker<check::PostCall, check::DeadSymbols> {
149150private:
150151 const std::array<MutexDescriptor, 8 > MutexDescriptors{
151152 // NOTE: There are standard library implementations where some methods
@@ -179,6 +180,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
179180 {CDM::CLibrary, {" read" }},
180181 {CDM::CLibrary, {" recv" }}};
181182
183+ const CallDescription OpenFunction{CDM::CLibrary, {" open" }, 2 };
184+
182185 const BugType BlockInCritSectionBugType{
183186 this , " Call to blocking function in critical section" , " Blocking Error" };
184187
@@ -197,6 +200,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
197200 void handleUnlock (const MutexDescriptor &Mutex, const CallEvent &Call,
198201 CheckerContext &C) const ;
199202
203+ void handleOpen (const CallEvent &Call, CheckerContext &C) const ;
204+
200205 [[nodiscard]] bool isBlockingInCritSection (const CallEvent &Call,
201206 CheckerContext &C) const ;
202207
@@ -205,11 +210,14 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
205210 // / Process lock.
206211 // / Process blocking functions (sleep, getc, fgets, read, recv)
207212 void checkPostCall (const CallEvent &Call, CheckerContext &C) const ;
213+
214+ void checkDeadSymbols (SymbolReaper &SymReaper, CheckerContext &C) const ;
208215};
209216
210217} // end anonymous namespace
211218
212219REGISTER_LIST_WITH_PROGRAMSTATE (ActiveCritSections, CritSectionMarker)
220+ REGISTER_SET_WITH_PROGRAMSTATE(NonBlockFileDescriptor, SymbolRef)
213221
214222// Iterator traits for ImmutableList data structure
215223// that enable the use of STL algorithms.
@@ -306,6 +314,25 @@ void BlockInCriticalSectionChecker::handleUnlock(
306314 C.addTransition (State);
307315}
308316
317+ void BlockInCriticalSectionChecker::handleOpen (const CallEvent &Call,
318+ CheckerContext &C) const {
319+ const auto *Flag = Call.getArgExpr (1 );
320+ static std::optional<int > ValueOfONonBlockVFlag =
321+ tryExpandAsInteger (" O_NONBLOCK" , C.getBugReporter ().getPreprocessor ());
322+ if (!ValueOfONonBlockVFlag)
323+ return ;
324+
325+ SVal FlagSV = C.getState ()->getSVal (Flag, C.getLocationContext ());
326+ const llvm::APSInt *FlagV = FlagSV.getAsInteger ();
327+ if (!FlagV)
328+ return ;
329+
330+ if ((*FlagV & ValueOfONonBlockVFlag.value ()) != 0 )
331+ if (SymbolRef SR = Call.getReturnValue ().getAsSymbol ()) {
332+ C.addTransition (C.getState ()->add <NonBlockFileDescriptor>(SR));
333+ }
334+ }
335+
309336bool BlockInCriticalSectionChecker::isBlockingInCritSection (
310337 const CallEvent &Call, CheckerContext &C) const {
311338 return BlockingFunctions.contains (Call) &&
@@ -315,16 +342,54 @@ bool BlockInCriticalSectionChecker::isBlockingInCritSection(
315342void BlockInCriticalSectionChecker::checkPostCall (const CallEvent &Call,
316343 CheckerContext &C) const {
317344 if (isBlockingInCritSection (Call, C)) {
345+ // for 'read' and 'recv' call, check whether it's file descriptor(first
346+ // argument) is
347+ // created by 'open' API with O_NONBLOCK flag or is equal to -1, they will
348+ // not cause block in these situations, don't report
349+ StringRef FuncName = Call.getCalleeIdentifier ()->getName ();
350+ if (FuncName == " read" || FuncName == " recv" ) {
351+ const auto *Arg = Call.getArgExpr (0 );
352+ if (!Arg)
353+ return ;
354+
355+ SVal SV = C.getSVal (Arg);
356+ if (const auto *IntValue = SV.getAsInteger ()) {
357+ if (*IntValue == -1 )
358+ return ;
359+ }
360+
361+ SymbolRef SR = C.getSVal (Arg).getAsSymbol ();
362+ if (SR && C.getState ()->contains <NonBlockFileDescriptor>(SR)) {
363+ return ;
364+ }
365+ }
318366 reportBlockInCritSection (Call, C);
319367 } else if (std::optional<MutexDescriptor> LockDesc =
320368 checkDescriptorMatch (Call, C, /* IsLock=*/ true )) {
321369 handleLock (*LockDesc, Call, C);
322370 } else if (std::optional<MutexDescriptor> UnlockDesc =
323371 checkDescriptorMatch (Call, C, /* IsLock=*/ false )) {
324372 handleUnlock (*UnlockDesc, Call, C);
373+ } else if (OpenFunction.matches (Call)) {
374+ handleOpen (Call, C);
325375 }
326376}
327377
378+ void BlockInCriticalSectionChecker::checkDeadSymbols (SymbolReaper &SymReaper,
379+ CheckerContext &C) const {
380+ ProgramStateRef State = C.getState ();
381+
382+ // Remove the dead symbols from the NonBlockFileDescriptor set.
383+ NonBlockFileDescriptorTy Tracked = State->get <NonBlockFileDescriptor>();
384+ for (SymbolRef SR : Tracked) {
385+ if (SymReaper.isDead (SR)) {
386+ State = State->remove <NonBlockFileDescriptor>(SR);
387+ }
388+ }
389+
390+ C.addTransition (State);
391+ }
392+
328393void BlockInCriticalSectionChecker::reportBlockInCritSection (
329394 const CallEvent &Call, CheckerContext &C) const {
330395 ExplodedNode *ErrNode = C.generateNonFatalErrorNode (C.getState ());
0 commit comments