@@ -145,6 +145,57 @@ using MutexDescriptor =
145
145
std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
146
146
RAIIMutexDescriptor>;
147
147
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
+
148
199
class BlockInCriticalSectionChecker : public Checker <check::PostCall> {
149
200
private:
150
201
const std::array<MutexDescriptor, 8 > MutexDescriptors{
@@ -182,6 +233,9 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
182
233
const BugType BlockInCritSectionBugType{
183
234
this , " Call to blocking function in critical section" , " Blocking Error" };
184
235
236
+ using O_NONBLOCKValueTy = std::optional<int >;
237
+ mutable std::optional<O_NONBLOCKValueTy> O_NONBLOCKValue;
238
+
185
239
void reportBlockInCritSection (const CallEvent &call, CheckerContext &C) const ;
186
240
187
241
[[nodiscard]] const NoteTag *createCritSectionNote (CritSectionMarker M,
@@ -337,6 +391,28 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
337
391
<< " ' inside of critical section" ;
338
392
auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
339
393
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
+ }
340
416
R->addRange (Call.getSourceRange ());
341
417
R->markInteresting (Call.getReturnValue ());
342
418
C.emitReport (std::move (R));
0 commit comments