26 #include "llvm/ADT/STLExtras.h"
27 #include "llvm/ADT/SmallString.h"
28 #include "llvm/ADT/StringExtras.h"
34 using namespace clang;
39 struct CritSectionMarker {
40 const Expr *LockExpr{};
43 void Profile(llvm::FoldingSetNodeID &
ID)
const {
48 [[nodiscard]] constexpr
bool
49 operator==(
const CritSectionMarker &Other)
const noexcept {
50 return LockExpr ==
Other.LockExpr && LockReg ==
Other.LockReg;
52 [[nodiscard]] constexpr
bool
53 operator!=(
const CritSectionMarker &Other)
const noexcept {
54 return !(*
this ==
Other);
58 class CallDescriptionBasedMatcher {
65 : LockFn(
std::move(LockFn)), UnlockFn(
std::move(UnlockFn)) {}
74 class FirstArgMutexDescriptor :
public CallDescriptionBasedMatcher {
77 : CallDescriptionBasedMatcher(
std::move(LockFn),
std::move(UnlockFn)) {}
80 return Call.getArgSVal(0).getAsRegion();
84 class MemberMutexDescriptor :
public CallDescriptionBasedMatcher {
87 : CallDescriptionBasedMatcher(
std::move(LockFn),
std::move(UnlockFn)) {}
90 return cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
94 class RAIIMutexDescriptor {
96 mutable bool IdentifierInfoInitialized{};
99 void initIdentifierInfo(
const CallEvent &Call)
const {
100 if (!IdentifierInfoInitialized) {
106 const auto &ASTCtx =
Call.getState()->getStateManager().getContext();
107 Guard = &ASTCtx.Idents.get(GuardName);
111 template <
typename T>
bool matchesImpl(
const CallEvent &Call)
const {
112 const T *
C = dyn_cast<T>(&Call);
116 cast<CXXRecordDecl>(
C->getDecl()->getParent())->getIdentifier();
121 RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
123 initIdentifierInfo(Call);
125 return matchesImpl<CXXConstructorCall>(Call);
127 return matchesImpl<CXXDestructorCall>(Call);
133 if (std::optional<SVal> Object =
Call.getReturnValueUnderConstruction()) {
134 LockRegion =
Object->getAsRegion();
137 LockRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
143 using MutexDescriptor =
144 std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
145 RAIIMutexDescriptor>;
147 class BlockInCriticalSectionChecker :
public Checker<check::PostCall> {
149 const std::array<MutexDescriptor, 8> MutexDescriptors{
151 {
"std",
"mutex",
"lock"},
154 FirstArgMutexDescriptor({
CDM::CLibrary, {
"pthread_mutex_lock"}, 1},
158 FirstArgMutexDescriptor({
CDM::CLibrary, {
"pthread_mutex_trylock"}, 1},
162 FirstArgMutexDescriptor({
CDM::CLibrary, {
"mtx_timedlock"}, 1},
164 RAIIMutexDescriptor(
"lock_guard"),
165 RAIIMutexDescriptor(
"unique_lock")};
173 const BugType BlockInCritSectionBugType{
174 this,
"Call to blocking function in critical section",
"Blocking Error"};
178 [[nodiscard]]
const NoteTag *createCritSectionNote(CritSectionMarker M,
181 [[nodiscard]] std::optional<MutexDescriptor>
185 void handleLock(
const MutexDescriptor &Mutex,
const CallEvent &Call,
188 void handleUnlock(
const MutexDescriptor &Mutex,
const CallEvent &Call,
191 [[nodiscard]]
bool isBlockingInCritSection(
const CallEvent &Call,
211 struct iterator_traits<
212 typename
llvm::ImmutableList<CritSectionMarker>::iterator> {
221 std::optional<MutexDescriptor>
222 BlockInCriticalSectionChecker::checkDescriptorMatch(
const CallEvent &Call,
225 const auto Descriptor =
226 llvm::find_if(MutexDescriptors, [&Call, IsLock](
auto &&Descriptor) {
228 [&Call, IsLock](
auto &&DescriptorImpl) {
229 return DescriptorImpl.matches(Call, IsLock);
233 if (Descriptor != MutexDescriptors.end())
239 const MutexDescriptor &Descriptor,
242 [&Call, IsLock](
auto &&Descriptor) {
243 return Descriptor.getRegion(Call, IsLock);
248 void BlockInCriticalSectionChecker::handleLock(
249 const MutexDescriptor &LockDescriptor,
const CallEvent &Call,
256 const CritSectionMarker MarkToAdd{
Call.getOriginExpr(), MutexRegion};
258 C.getState()->add<ActiveCritSections>(MarkToAdd);
259 C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
262 void BlockInCriticalSectionChecker::handleUnlock(
263 const MutexDescriptor &UnlockDescriptor,
const CallEvent &Call,
266 getRegion(Call, UnlockDescriptor,
false);
271 const auto ActiveSections =
State->get<ActiveCritSections>();
272 const auto MostRecentLock =
273 llvm::find_if(ActiveSections, [MutexRegion](
auto &&Marker) {
274 return Marker.LockReg == MutexRegion;
276 if (MostRecentLock == ActiveSections.end())
280 auto &Factory =
State->get_context<ActiveCritSections>();
281 llvm::ImmutableList<CritSectionMarker> NewList = Factory.getEmptyList();
282 for (
auto It = ActiveSections.begin(),
End = ActiveSections.end(); It !=
End;
284 if (It != MostRecentLock)
285 NewList = Factory.add(*It, NewList);
288 State =
State->set<ActiveCritSections>(NewList);
292 bool BlockInCriticalSectionChecker::isBlockingInCritSection(
294 return BlockingFunctions.contains(Call) &&
295 !
C.getState()->get<ActiveCritSections>().isEmpty();
298 void BlockInCriticalSectionChecker::checkPostCall(
const CallEvent &Call,
300 if (isBlockingInCritSection(Call, C)) {
301 reportBlockInCritSection(Call, C);
302 }
else if (std::optional<MutexDescriptor> LockDesc =
303 checkDescriptorMatch(Call, C,
true)) {
304 handleLock(*LockDesc, Call, C);
305 }
else if (std::optional<MutexDescriptor> UnlockDesc =
306 checkDescriptorMatch(Call, C,
false)) {
307 handleUnlock(*UnlockDesc, Call, C);
311 void BlockInCriticalSectionChecker::reportBlockInCritSection(
313 ExplodedNode *ErrNode =
C.generateNonFatalErrorNode(
C.getState());
318 llvm::raw_string_ostream os(msg);
319 os <<
"Call to blocking function '" <<
Call.getCalleeIdentifier()->getName()
320 <<
"' inside of critical section";
321 auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
323 R->addRange(
Call.getSourceRange());
324 R->markInteresting(
Call.getReturnValue());
325 C.emitReport(std::move(R));
329 BlockInCriticalSectionChecker::createCritSectionNote(CritSectionMarker M,
331 const BugType *BT = &this->BlockInCritSectionBugType;
333 llvm::raw_ostream &OS) {
338 const auto CritSectionBegins =
342 CritSectionBegins, std::back_inserter(LocksForMutex),
343 [M](
const auto &Marker) { return Marker.LockReg == M.LockReg; });
344 if (LocksForMutex.empty())
349 std::reverse(LocksForMutex.begin(), LocksForMutex.end());
353 const auto Position =
354 llvm::find_if(std::as_const(LocksForMutex), [M](
const auto &Marker) {
355 return Marker.LockExpr == M.LockExpr;
357 if (Position == LocksForMutex.end())
362 if (LocksForMutex.size() == 1) {
363 OS <<
"Entering critical section here";
367 const auto IndexOfLock =
368 std::distance(std::as_const(LocksForMutex).begin(), Position);
370 const auto OrdinalOfLock = IndexOfLock + 1;
371 OS <<
"Entering critical section for the " << OrdinalOfLock
372 << llvm::getOrdinalSuffix(OrdinalOfLock) <<
" time here";
376 void ento::registerBlockInCriticalSectionChecker(
CheckerManager &mgr) {
380 bool ento::shouldRegisterBlockInCriticalSectionChecker(
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
#define REGISTER_LIST_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable list type NameTy, suitable for placement into the ProgramState.
__PTRDIFF_TYPE__ ptrdiff_t
This represents one expression.
One of these records is kept for each identifier that is lexed.
const BugType & getBugType() const
An immutable set of CallDescriptions.
A CallDescription is a pattern that can be used to match calls based on the qualified name and the ar...
bool matches(const CallEvent &Call) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
@ CLibrary
Match calls to functions from the C standard library.
@ CXXMethod
Matches a C++ method (may be static, may be virtual, may be an overloaded operator,...
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
const ProgramStateRef & getState() const
MemRegion - The root abstract class for all memory regions.
The tag upon which the TagVisitor reacts.
const ExplodedNode * getErrorNode() const
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
bool matches(const til::SExpr *E1, const til::SExpr *E2)
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
bool operator!=(CanQual< T > x, CanQual< U > y)
const FunctionProtoType * T
@ Other
Other implicit parameter.
Diagnostic wrappers for TextAPI types for error reporting.
float __ovld __cnfn distance(float, float)
Returns the distance between p0 and p1.
std::ptrdiff_t difference_type
CritSectionMarker & reference
CritSectionMarker * pointer
CritSectionMarker value_type
std::forward_iterator_tag iterator_category