@@ -145,6 +145,57 @@ using MutexDescriptor =
145145 std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
146146 RAIIMutexDescriptor>;
147147
148+ class SuppressNonBlockingStreams : public BugReporterVisitor {
149+ private:
150+ const CallDescription OpenFunction{CDM::CLibrary, {" open" }, 2 };
151+ SymbolRef StreamSym;
152+ const int NonBlockMacroVal;
153+ bool Satisfied = false ;
154+
155+ public:
156+ SuppressNonBlockingStreams (SymbolRef StreamSym, int NonBlockMacroVal)
157+ : StreamSym(StreamSym), NonBlockMacroVal(NonBlockMacroVal) {}
158+
159+ static void *getTag () {
160+ static bool Tag;
161+ return &Tag;
162+ }
163+
164+ void Profile (llvm::FoldingSetNodeID &ID) const override {
165+ ID.AddPointer (getTag ());
166+ }
167+
168+ PathDiagnosticPieceRef VisitNode (const ExplodedNode *N,
169+ BugReporterContext &BRC,
170+ PathSensitiveBugReport &BR) override {
171+ if (Satisfied)
172+ return nullptr ;
173+
174+ std::optional<StmtPoint> Point = N->getLocationAs <StmtPoint>();
175+ if (!Point)
176+ return nullptr ;
177+
178+ const auto *CE = Point->getStmtAs <CallExpr>();
179+ if (!CE || !OpenFunction.matchesAsWritten (*CE))
180+ return nullptr ;
181+
182+ if (N->getSVal (CE).getAsSymbol () != StreamSym)
183+ return nullptr ;
184+
185+ Satisfied = true ;
186+
187+ // Check if open's second argument contains O_NONBLOCK
188+ const llvm::APSInt *FlagVal = N->getSVal (CE->getArg (1 )).getAsInteger ();
189+ if (!FlagVal)
190+ return nullptr ;
191+
192+ if ((*FlagVal & NonBlockMacroVal) != 0 )
193+ BR.markInvalid (getTag (), nullptr );
194+
195+ return nullptr ;
196+ }
197+ };
198+
148199class BlockInCriticalSectionChecker : public Checker <check::PostCall> {
149200private:
150201 const std::array<MutexDescriptor, 8 > MutexDescriptors{
@@ -182,6 +233,9 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
182233 const BugType BlockInCritSectionBugType{
183234 this , " Call to blocking function in critical section" , " Blocking Error" };
184235
236+ using O_NONBLOCKValueTy = std::optional<int >;
237+ mutable std::optional<O_NONBLOCKValueTy> O_NONBLOCKValue;
238+
185239 void reportBlockInCritSection (const CallEvent &call, CheckerContext &C) const ;
186240
187241 [[nodiscard]] const NoteTag *createCritSectionNote (CritSectionMarker M,
@@ -337,6 +391,28 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
337391 << " ' inside of critical section" ;
338392 auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
339393 os.str (), ErrNode);
394+ // for 'read' and 'recv' call, check whether it's file descriptor(first
395+ // argument) is
396+ // created by 'open' API with O_NONBLOCK flag or is equal to -1, they will
397+ // not cause block in these situations, don't report
398+ StringRef FuncName = Call.getCalleeIdentifier ()->getName ();
399+ if (FuncName == " read" || FuncName == " recv" ) {
400+ SVal SV = Call.getArgSVal (0 );
401+ SValBuilder &SVB = C.getSValBuilder ();
402+ ProgramStateRef state = C.getState ();
403+ ConditionTruthVal CTV =
404+ state->areEqual (SV, SVB.makeIntVal (-1 , C.getASTContext ().IntTy ));
405+ if (CTV.isConstrainedTrue ())
406+ return ;
407+
408+ if (SymbolRef SR = SV.getAsSymbol ()) {
409+ if (!O_NONBLOCKValue)
410+ O_NONBLOCKValue = tryExpandAsInteger (
411+ " O_NONBLOCK" , C.getBugReporter ().getPreprocessor ());
412+ if (*O_NONBLOCKValue)
413+ R->addVisitor <SuppressNonBlockingStreams>(SR, **O_NONBLOCKValue);
414+ }
415+ }
340416 R->addRange (Call.getSourceRange ());
341417 R->markInteresting (Call.getReturnValue ());
342418 C.emitReport (std::move (R));
0 commit comments