@@ -145,8 +145,63 @@ using MutexDescriptor =
145145 std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
146146 RAIIMutexDescriptor>;
147147
148- class BlockInCriticalSectionChecker
149- : public Checker<check::PostCall, check::DeadSymbols> {
148+ class NonBlockOpenVisitor : public BugReporterVisitor {
149+ private:
150+ SymbolRef SR;
151+ int O_NONBLOCKValue;
152+ const CallDescription OpenFunction;
153+ bool Satisfied;
154+
155+ public:
156+ NonBlockOpenVisitor (SymbolRef SR, int O_NONBLOCKValue)
157+ : SR(SR), O_NONBLOCKValue(O_NONBLOCKValue),
158+ OpenFunction (CDM::CLibrary, {" open" }, 2 ), Satisfied(false ) {}
159+
160+ static void *getTag () {
161+ static int Tag = 0 ;
162+ return static_cast <void *>(&Tag);
163+ }
164+
165+ void Profile (llvm::FoldingSetNodeID &ID) const override {
166+ ID.AddPointer (getTag ());
167+ ID.AddPointer (SR);
168+ }
169+
170+ PathDiagnosticPieceRef VisitNode (const ExplodedNode *N,
171+ BugReporterContext &BRC,
172+ PathSensitiveBugReport &BR) override {
173+ if (Satisfied)
174+ return nullptr ;
175+
176+ std::optional<StmtPoint> Point = N->getLocation ().getAs <StmtPoint>();
177+ if (!Point)
178+ return nullptr ;
179+
180+ auto *CE = dyn_cast<CallExpr>(Point->getStmt ());
181+ if (!CE || !OpenFunction.matchesAsWritten (*CE))
182+ return nullptr ;
183+
184+ SVal SV = N->getSVal (CE);
185+ if (SV.getAsSymbol () != SR)
186+ return nullptr ;
187+
188+ Satisfied = true ;
189+
190+ // Check if open's second argument contains O_NONBLOCK
191+ const auto *Flag = CE->getArg (1 );
192+ SVal FlagSV = N->getSVal (Flag);
193+ const llvm::APSInt *FlagV = FlagSV.getAsInteger ();
194+ if (!FlagV)
195+ return nullptr ;
196+
197+ if ((*FlagV & O_NONBLOCKValue) != 0 )
198+ BR.markInvalid (getTag (), nullptr );
199+
200+ return nullptr ;
201+ }
202+ };
203+
204+ class BlockInCriticalSectionChecker : public Checker <check::PostCall> {
150205private:
151206 const std::array<MutexDescriptor, 8 > MutexDescriptors{
152207 // NOTE: There are standard library implementations where some methods
@@ -180,8 +235,6 @@ class BlockInCriticalSectionChecker
180235 {CDM::CLibrary, {" read" }},
181236 {CDM::CLibrary, {" recv" }}};
182237
183- const CallDescription OpenFunction{CDM::CLibrary, {" open" }, 2 };
184-
185238 const BugType BlockInCritSectionBugType{
186239 this , " Call to blocking function in critical section" , " Blocking Error" };
187240
@@ -203,8 +256,6 @@ class BlockInCriticalSectionChecker
203256 void handleUnlock (const MutexDescriptor &Mutex, const CallEvent &Call,
204257 CheckerContext &C) const ;
205258
206- void handleOpen (const CallEvent &Call, CheckerContext &C) const ;
207-
208259 [[nodiscard]] bool isBlockingInCritSection (const CallEvent &Call,
209260 CheckerContext &C) const ;
210261
@@ -213,14 +264,11 @@ class BlockInCriticalSectionChecker
213264 // / Process lock.
214265 // / Process blocking functions (sleep, getc, fgets, read, recv)
215266 void checkPostCall (const CallEvent &Call, CheckerContext &C) const ;
216-
217- void checkDeadSymbols (SymbolReaper &SymReaper, CheckerContext &C) const ;
218267};
219268
220269} // end anonymous namespace
221270
222271REGISTER_LIST_WITH_PROGRAMSTATE (ActiveCritSections, CritSectionMarker)
223- REGISTER_SET_WITH_PROGRAMSTATE(NonBlockFileDescriptor, SymbolRef)
224272
225273// Iterator traits for ImmutableList data structure
226274// that enable the use of STL algorithms.
@@ -317,24 +365,6 @@ void BlockInCriticalSectionChecker::handleUnlock(
317365 C.addTransition (State);
318366}
319367
320- void BlockInCriticalSectionChecker::handleOpen (const CallEvent &Call,
321- CheckerContext &C) const {
322- const auto *Flag = Call.getArgExpr (1 );
323- SVal FlagSV = C.getState ()->getSVal (Flag, C.getLocationContext ());
324- const llvm::APSInt *FlagV = FlagSV.getAsInteger ();
325- if (!FlagV)
326- return ;
327-
328- if (!O_NONBLOCKValue)
329- O_NONBLOCKValue =
330- tryExpandAsInteger (" O_NONBLOCK" , C.getBugReporter ().getPreprocessor ());
331-
332- if (*O_NONBLOCKValue && ((*FlagV & **O_NONBLOCKValue) != 0 ))
333- if (SymbolRef SR = Call.getReturnValue ().getAsSymbol ()) {
334- C.addTransition (C.getState ()->add <NonBlockFileDescriptor>(SR));
335- }
336- }
337-
338368bool BlockInCriticalSectionChecker::isBlockingInCritSection (
339369 const CallEvent &Call, CheckerContext &C) const {
340370 return BlockingFunctions.contains (Call) &&
@@ -344,52 +374,16 @@ bool BlockInCriticalSectionChecker::isBlockingInCritSection(
344374void BlockInCriticalSectionChecker::checkPostCall (const CallEvent &Call,
345375 CheckerContext &C) const {
346376 if (isBlockingInCritSection (Call, C)) {
347- // for 'read' and 'recv' call, check whether it's file descriptor(first
348- // argument) is
349- // created by 'open' API with O_NONBLOCK flag or is equal to -1, they will
350- // not cause block in these situations, don't report
351- StringRef FuncName = Call.getCalleeIdentifier ()->getName ();
352- if (FuncName == " read" || FuncName == " recv" ) {
353- SVal SV = Call.getArgSVal (0 );
354- SValBuilder &SVB = C.getSValBuilder ();
355- ProgramStateRef state = C.getState ();
356- ConditionTruthVal CTV =
357- state->areEqual (SV, SVB.makeIntVal (-1 , C.getASTContext ().IntTy ));
358- if (CTV.isConstrainedTrue ())
359- return ;
360-
361- SymbolRef SR = SV.getAsSymbol ();
362- if (SR && state->contains <NonBlockFileDescriptor>(SR)) {
363- return ;
364- }
365- }
366377 reportBlockInCritSection (Call, C);
367378 } else if (std::optional<MutexDescriptor> LockDesc =
368379 checkDescriptorMatch (Call, C, /* IsLock=*/ true )) {
369380 handleLock (*LockDesc, Call, C);
370381 } else if (std::optional<MutexDescriptor> UnlockDesc =
371382 checkDescriptorMatch (Call, C, /* IsLock=*/ false )) {
372383 handleUnlock (*UnlockDesc, Call, C);
373- } else if (OpenFunction.matches (Call)) {
374- handleOpen (Call, C);
375384 }
376385}
377386
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-
393387void BlockInCriticalSectionChecker::reportBlockInCritSection (
394388 const CallEvent &Call, CheckerContext &C) const {
395389 ExplodedNode *ErrNode = C.generateNonFatalErrorNode (C.getState ());
@@ -402,6 +396,28 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
402396 << " ' inside of critical section" ;
403397 auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
404398 os.str (), ErrNode);
399+ // for 'read' and 'recv' call, check whether it's file descriptor(first
400+ // argument) is
401+ // created by 'open' API with O_NONBLOCK flag or is equal to -1, they will
402+ // not cause block in these situations, don't report
403+ StringRef FuncName = Call.getCalleeIdentifier ()->getName ();
404+ if (FuncName == " read" || FuncName == " recv" ) {
405+ SVal SV = Call.getArgSVal (0 );
406+ SValBuilder &SVB = C.getSValBuilder ();
407+ ProgramStateRef state = C.getState ();
408+ ConditionTruthVal CTV =
409+ state->areEqual (SV, SVB.makeIntVal (-1 , C.getASTContext ().IntTy ));
410+ if (CTV.isConstrainedTrue ())
411+ return ;
412+
413+ if (SymbolRef SR = SV.getAsSymbol ()) {
414+ if (!O_NONBLOCKValue)
415+ O_NONBLOCKValue = tryExpandAsInteger (
416+ " O_NONBLOCK" , C.getBugReporter ().getPreprocessor ());
417+ if (*O_NONBLOCKValue)
418+ R->addVisitor <NonBlockOpenVisitor>(SR, **O_NONBLOCKValue);
419+ }
420+ }
405421 R->addRange (Call.getSourceRange ());
406422 R->markInteresting (Call.getReturnValue ());
407423 C.emitReport (std::move (R));
0 commit comments