@@ -145,93 +145,8 @@ using MutexDescriptor =
145
145
std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
146
146
RAIIMutexDescriptor>;
147
147
148
- class NonBlockOpenVisitor : public BugReporterVisitor {
149
- private:
150
- const VarRegion *VR;
151
- const CallExpr *OpenCallExpr;
152
- int O_NONBLOCKV;
153
- bool Satisfied;
154
- CallDescription OpenFunction{CDM::CLibrary, {" open" }, 2 };
155
-
156
- public:
157
- NonBlockOpenVisitor (const VarRegion *VR, int O_NONBLOCKV)
158
- : VR(VR), OpenCallExpr(nullptr ), O_NONBLOCKV(O_NONBLOCKV),
159
- Satisfied (false ) {}
160
-
161
- static void *getTag () {
162
- static int Tag = 0 ;
163
- return static_cast <void *>(&Tag);
164
- }
165
-
166
- void Profile (llvm::FoldingSetNodeID &ID) const override {
167
- ID.AddPointer (getTag ());
168
- ID.AddPointer (VR);
169
- }
170
-
171
- PathDiagnosticPieceRef VisitNode (const ExplodedNode *N,
172
- BugReporterContext &BRC,
173
- PathSensitiveBugReport &BR) override {
174
- if (Satisfied)
175
- return nullptr ;
176
-
177
- // check for open call's 2th argument
178
- if (std::optional<StmtPoint> P = N->getLocationAs <StmtPoint>()) {
179
- if (OpenCallExpr && OpenCallExpr == P->getStmtAs <CallExpr>()) {
180
- Satisfied = true ;
181
- const auto *openFlagExpr = OpenCallExpr->getArg (1 );
182
- SVal flagSV = N->getSVal (openFlagExpr);
183
- const llvm::APSInt *flagV = flagSV.getAsInteger ();
184
- if (!flagV)
185
- return nullptr ;
186
-
187
- if ((*flagV & O_NONBLOCKV) != 0 )
188
- BR.markInvalid (getTag (), nullptr );
189
-
190
- return nullptr ;
191
- }
192
- }
193
-
194
- const ExplodedNode *Pred = N->getFirstPred ();
195
- SVal presv = Pred->getState ()->getSVal (VR);
196
- SVal sv = N->getState ()->getSVal (VR);
197
-
198
- // check if file descirptor's SVal changed between nodes
199
- if (sv == presv)
200
- return nullptr ;
201
-
202
- std::optional<PostStore> P = N->getLocationAs <PostStore>();
203
- if (!P)
204
- return nullptr ;
205
-
206
- if (const auto *DS = P->getStmtAs <DeclStmt>()) {
207
- // variable initialization
208
- // int fd = open(...)
209
- const VarDecl *VD = VR->getDecl ();
210
- if (DS->getSingleDecl () == VR->getDecl ()) {
211
- const auto *InitCall = dyn_cast_if_present<CallExpr>(VD->getInit ());
212
- if (!InitCall || !OpenFunction.matchesAsWritten (*InitCall))
213
- return nullptr ;
214
-
215
- OpenCallExpr = InitCall;
216
- }
217
- } else if (const auto *BO = P->getStmtAs <BinaryOperator>()) {
218
- // assignment
219
- // fd = open(...)
220
- const auto *DRE = dyn_cast<DeclRefExpr>(BO->getLHS ());
221
- if (DRE && DRE->getDecl () == VR->getDecl ()) {
222
- const auto *InitCall = dyn_cast<CallExpr>(BO->getRHS ());
223
- if (!InitCall || !OpenFunction.matchesAsWritten (*InitCall))
224
- return nullptr ;
225
-
226
- OpenCallExpr = InitCall;
227
- }
228
- }
229
-
230
- return nullptr ;
231
- }
232
- };
233
-
234
- class BlockInCriticalSectionChecker : public Checker <check::PostCall> {
148
+ class BlockInCriticalSectionChecker
149
+ : public Checker<check::PostCall, check::DeadSymbols> {
235
150
private:
236
151
const std::array<MutexDescriptor, 8 > MutexDescriptors{
237
152
// NOTE: There are standard library implementations where some methods
@@ -265,6 +180,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
265
180
{CDM::CLibrary, {" read" }},
266
181
{CDM::CLibrary, {" recv" }}};
267
182
183
+ const CallDescription OpenFunction{CDM::CLibrary, {" open" }, 2 };
184
+
268
185
const BugType BlockInCritSectionBugType{
269
186
this , " Call to blocking function in critical section" , " Blocking Error" };
270
187
@@ -283,6 +200,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
283
200
void handleUnlock (const MutexDescriptor &Mutex, const CallEvent &Call,
284
201
CheckerContext &C) const ;
285
202
203
+ void handleOpen (const CallEvent &Call, CheckerContext &C) const ;
204
+
286
205
[[nodiscard]] bool isBlockingInCritSection (const CallEvent &Call,
287
206
CheckerContext &C) const ;
288
207
@@ -291,11 +210,14 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
291
210
// / Process lock.
292
211
// / Process blocking functions (sleep, getc, fgets, read, recv)
293
212
void checkPostCall (const CallEvent &Call, CheckerContext &C) const ;
213
+
214
+ void checkDeadSymbols (SymbolReaper &SymReaper, CheckerContext &C) const ;
294
215
};
295
216
296
217
} // end anonymous namespace
297
218
298
219
REGISTER_LIST_WITH_PROGRAMSTATE (ActiveCritSections, CritSectionMarker)
220
+ REGISTER_SET_WITH_PROGRAMSTATE(NonBlockFileDescriptor, SymbolRef)
299
221
300
222
// Iterator traits for ImmutableList data structure
301
223
// that enable the use of STL algorithms.
@@ -392,6 +314,25 @@ void BlockInCriticalSectionChecker::handleUnlock(
392
314
C.addTransition (State);
393
315
}
394
316
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
+
395
336
bool BlockInCriticalSectionChecker::isBlockingInCritSection (
396
337
const CallEvent &Call, CheckerContext &C) const {
397
338
return BlockingFunctions.contains (Call) &&
@@ -401,16 +342,54 @@ bool BlockInCriticalSectionChecker::isBlockingInCritSection(
401
342
void BlockInCriticalSectionChecker::checkPostCall (const CallEvent &Call,
402
343
CheckerContext &C) const {
403
344
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
+ }
404
366
reportBlockInCritSection (Call, C);
405
367
} else if (std::optional<MutexDescriptor> LockDesc =
406
368
checkDescriptorMatch (Call, C, /* IsLock=*/ true )) {
407
369
handleLock (*LockDesc, Call, C);
408
370
} else if (std::optional<MutexDescriptor> UnlockDesc =
409
371
checkDescriptorMatch (Call, C, /* IsLock=*/ false )) {
410
372
handleUnlock (*UnlockDesc, Call, C);
373
+ } else if (OpenFunction.matches (Call)) {
374
+ handleOpen (Call, C);
411
375
}
412
376
}
413
377
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
+
414
393
void BlockInCriticalSectionChecker::reportBlockInCritSection (
415
394
const CallEvent &Call, CheckerContext &C) const {
416
395
ExplodedNode *ErrNode = C.generateNonFatalErrorNode (C.getState ());
@@ -425,36 +404,6 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
425
404
os.str (), ErrNode);
426
405
R->addRange (Call.getSourceRange ());
427
406
R->markInteresting (Call.getReturnValue ());
428
- // for 'read' call, check whether it's file descriptor(first argument) is
429
- // created by 'open' API with O_NONBLOCK flag and don't report for this
430
- // situation.
431
- if (Call.getCalleeIdentifier ()->getName () == " read" ) {
432
- do {
433
- const auto *arg = Call.getArgExpr (0 );
434
- if (!arg)
435
- break ;
436
-
437
- const auto *DRE = dyn_cast<DeclRefExpr>(arg->IgnoreImpCasts ());
438
- if (!DRE)
439
- break ;
440
-
441
- const auto *VD = dyn_cast_if_present<VarDecl>(DRE->getDecl ());
442
- if (!VD)
443
- break ;
444
-
445
- const VarRegion *VR = C.getState ()->getRegion (VD, C.getLocationContext ());
446
- if (!VR)
447
- break ;
448
-
449
- std::optional<int > O_NONBLOCKV = tryExpandAsInteger (
450
- " O_NONBLOCK" , C.getBugReporter ().getPreprocessor ());
451
- if (!O_NONBLOCKV)
452
- break ;
453
-
454
- R->addVisitor (
455
- std::make_unique<NonBlockOpenVisitor>(VR, O_NONBLOCKV.value ()));
456
- } while (false );
457
- }
458
407
C.emitReport (std::move (R));
459
408
}
460
409
0 commit comments