Skip to content

Commit edac8dc

Browse files
committed
use BugReportVisitor instead set
1 parent 20bec01 commit edac8dc

File tree

1 file changed

+79
-63
lines changed

1 file changed

+79
-63
lines changed

clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp

Lines changed: 79 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -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> {
150205
private:
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

222271
REGISTER_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-
338368
bool BlockInCriticalSectionChecker::isBlockingInCritSection(
339369
const CallEvent &Call, CheckerContext &C) const {
340370
return BlockingFunctions.contains(Call) &&
@@ -344,52 +374,16 @@ bool BlockInCriticalSectionChecker::isBlockingInCritSection(
344374
void 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-
393387
void 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

Comments
 (0)