Skip to content

Commit 551b041

Browse files
committed
implement with set and skip -1 file descriptor too
1 parent 021ebc6 commit 551b041

File tree

1 file changed

+66
-117
lines changed

1 file changed

+66
-117
lines changed

clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp

Lines changed: 66 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -145,93 +145,8 @@ using MutexDescriptor =
145145
std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
146146
RAIIMutexDescriptor>;
147147

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> {
235150
private:
236151
const std::array<MutexDescriptor, 8> MutexDescriptors{
237152
// NOTE: There are standard library implementations where some methods
@@ -265,6 +180,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
265180
{CDM::CLibrary, {"read"}},
266181
{CDM::CLibrary, {"recv"}}};
267182

183+
const CallDescription OpenFunction{CDM::CLibrary, {"open"}, 2};
184+
268185
const BugType BlockInCritSectionBugType{
269186
this, "Call to blocking function in critical section", "Blocking Error"};
270187

@@ -283,6 +200,8 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
283200
void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
284201
CheckerContext &C) const;
285202

203+
void handleOpen(const CallEvent &Call, CheckerContext &C) const;
204+
286205
[[nodiscard]] bool isBlockingInCritSection(const CallEvent &Call,
287206
CheckerContext &C) const;
288207

@@ -291,11 +210,14 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
291210
/// Process lock.
292211
/// Process blocking functions (sleep, getc, fgets, read, recv)
293212
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
213+
214+
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
294215
};
295216

296217
} // end anonymous namespace
297218

298219
REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
220+
REGISTER_SET_WITH_PROGRAMSTATE(NonBlockFileDescriptor, SymbolRef)
299221

300222
// Iterator traits for ImmutableList data structure
301223
// that enable the use of STL algorithms.
@@ -392,6 +314,25 @@ void BlockInCriticalSectionChecker::handleUnlock(
392314
C.addTransition(State);
393315
}
394316

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+
395336
bool BlockInCriticalSectionChecker::isBlockingInCritSection(
396337
const CallEvent &Call, CheckerContext &C) const {
397338
return BlockingFunctions.contains(Call) &&
@@ -401,16 +342,54 @@ bool BlockInCriticalSectionChecker::isBlockingInCritSection(
401342
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
402343
CheckerContext &C) const {
403344
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+
}
404366
reportBlockInCritSection(Call, C);
405367
} else if (std::optional<MutexDescriptor> LockDesc =
406368
checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
407369
handleLock(*LockDesc, Call, C);
408370
} else if (std::optional<MutexDescriptor> UnlockDesc =
409371
checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
410372
handleUnlock(*UnlockDesc, Call, C);
373+
} else if (OpenFunction.matches(Call)) {
374+
handleOpen(Call, C);
411375
}
412376
}
413377

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+
414393
void BlockInCriticalSectionChecker::reportBlockInCritSection(
415394
const CallEvent &Call, CheckerContext &C) const {
416395
ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
@@ -425,36 +404,6 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
425404
os.str(), ErrNode);
426405
R->addRange(Call.getSourceRange());
427406
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-
}
458407
C.emitReport(std::move(R));
459408
}
460409

0 commit comments

Comments
 (0)