|
25 | 25 | #include "llvm/Support/Casting.h" |
26 | 26 | #include "llvm/Support/Compiler.h" |
27 | 27 | #include "llvm/Support/SaveAndRestore.h" |
| 28 | +#include <clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h> |
28 | 29 | #include <optional> |
29 | 30 |
|
30 | 31 | using namespace clang; |
@@ -532,6 +533,71 @@ void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, |
532 | 533 | inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State); |
533 | 534 | } |
534 | 535 |
|
| 536 | +// XXX: We only handle pthreads currently |
| 537 | +#pragma clang optimize off |
| 538 | +void ExprEngine::threadBifurcate(CallEvent const &Call, Decl const *D, |
| 539 | + NodeBuilder &Bldr, ExplodedNode *Pred, |
| 540 | + ProgramStateRef State) { |
| 541 | + // The declaration for pthread_create(3): |
| 542 | + /* int pthread_create(pthread_t *thread, |
| 543 | + const pthread_attr_t *attr, |
| 544 | + void *(*start_routine)(void *), |
| 545 | + void *arg); |
| 546 | + */ |
| 547 | + assert(Call.getNumArgs() == 4 && "pthread_create(3) should have 4 args"); |
| 548 | + |
| 549 | + // 1. Extract the expr for the thread's start routine |
| 550 | + auto const *SRExpr = Call.getArgExpr(2); |
| 551 | + assert(SRExpr && "start_routine should exist"); |
| 552 | + auto const *SRInit = Call.getArgExpr(3); |
| 553 | + assert(SRInit && "start_routine should have an init param"); |
| 554 | + |
| 555 | + // 2. Convert the expr into a function pointer |
| 556 | + auto const SRV = Pred->getSVal(SRExpr); |
| 557 | + auto const *SRR = SRV.getAsRegion(); |
| 558 | + assert(SRR && "start_routine should be a pointer"); |
| 559 | + |
| 560 | + FunctionDecl const *StartRoutine = nullptr; |
| 561 | + if (auto const *FR = dyn_cast<FunctionCodeRegion>(SRR)) |
| 562 | + StartRoutine = dyn_cast<FunctionDecl>(FR->getDecl()); |
| 563 | + |
| 564 | + assert(StartRoutine && "start_routine should be a valid function pointer"); |
| 565 | + assert(StartRoutine->hasBody() && "start_routine must be well defined"); |
| 566 | + |
| 567 | + // 3. Create a CallEvent for calling the function pointer |
| 568 | + |
| 569 | + auto *SRCtx = AMgr.getAnalysisDeclContext(StartRoutine); |
| 570 | + |
| 571 | + auto &CEMgr = State->getStateManager().getCallEventManager(); |
| 572 | + auto const *LC = Pred->getLocationContext(); |
| 573 | + |
| 574 | + // We need to construct a fake CallExpr for the worker thread, since it doesn't exist |
| 575 | + |
| 576 | + auto *srexpr = DeclRefExpr::Create( |
| 577 | + SRR->getContext(), |
| 578 | + NestedNameSpecifierLoc(), |
| 579 | + SourceLocation(), |
| 580 | + const_cast<FunctionDecl*>(StartRoutine), |
| 581 | + false, |
| 582 | + SourceLocation(), |
| 583 | + StartRoutine->getType(), |
| 584 | + VK_LValue); |
| 585 | + |
| 586 | + CallExpr *srcall = CallExpr::Create( |
| 587 | + SRR->getContext(), |
| 588 | + srexpr, |
| 589 | + {const_cast<Expr*>(SRInit)}, |
| 590 | + StartRoutine->getType(), |
| 591 | + VK_LValue, |
| 592 | +SourceLocation(), |
| 593 | +FPOptionsOverride()); |
| 594 | + |
| 595 | + auto call = CEMgr.getSimpleCall(srcall, State, LC, getCFGElementRef()); |
| 596 | + |
| 597 | + inlineCall(Engine.getWorkList(), *call, StartRoutine, Bldr, Pred, State); |
| 598 | +} |
| 599 | +#pragma clang optimize on |
| 600 | + |
535 | 601 | void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call, |
536 | 602 | const Decl *D, NodeBuilder &Bldr, |
537 | 603 | ExplodedNode *Pred, ProgramStateRef State) { |
@@ -1144,6 +1210,14 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, |
1144 | 1210 | return true; |
1145 | 1211 | } |
1146 | 1212 |
|
| 1213 | +static const CallDescriptionSet ThreadCreateCalls { |
| 1214 | + { CDM::CLibrary, {"pthread_create"}, 4}, |
| 1215 | +}; |
| 1216 | + |
| 1217 | +bool ExprEngine::isThread(CallEvent const &Call) const { |
| 1218 | + return ThreadCreateCalls.contains(Call); |
| 1219 | +} |
| 1220 | + |
1147 | 1221 | bool ExprEngine::shouldInlineArrayConstruction(const ProgramStateRef State, |
1148 | 1222 | const CXXConstructExpr *CE, |
1149 | 1223 | const LocationContext *LCtx) { |
@@ -1241,6 +1315,14 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, |
1241 | 1315 | RuntimeDefinition RD = Call->getRuntimeDefinition(); |
1242 | 1316 | Call->setForeign(RD.isForeign()); |
1243 | 1317 | const Decl *D = RD.getDecl(); |
| 1318 | + |
| 1319 | + // TODO: make this a proper mode |
| 1320 | + // Special case thread creation |
| 1321 | + if (isThread(*Call)) { |
| 1322 | + threadBifurcate(*Call, D, Bldr, Pred, State); |
| 1323 | + return; |
| 1324 | + } |
| 1325 | + |
1244 | 1326 | if (shouldInlineCall(*Call, D, Pred, CallOpts)) { |
1245 | 1327 | if (RD.mayHaveOtherDefinitions()) { |
1246 | 1328 | AnalyzerOptions &Options = getAnalysisManager().options; |
|
0 commit comments