33// Assumptions for pinning:
44// * args need to be pinned
55// * JL_ROOTING_ARGUMENT and JL_ROOTED_ARGUMENT will propagate pinning state as well.
6+ // * The checker may not consider alias for derived pointers in some cases.
7+ // * if f(x) returns a derived pointer from x, a = f(x); b = f(x); PTR_PIN(a); The checker will NOT find b as pinned.
8+ // * a = x->y; b = x->y; PTR_PIN(a); The checker will find b as pinned.
9+ // * Need to see if this affects correctness.
10+ // * The checker may report some vals as moved even if there is a new load for the val after safepoint.
11+ // * f(x->a); jl_safepoint(); f(x->a); x->a is loaded after a safepoint, but the checker may report errors. This seems fine, as the compiler may hoist the load.
12+ // * a = x->a; f(a); jl_safepoint(); f(a); a may be moved in a safepoint, and the checker will report errors.
613
714#include " clang/Frontend/FrontendActions.h"
815#include " clang/StaticAnalyzer/Checkers/SValExplainer.h"
@@ -96,6 +103,7 @@ class GCChecker
96103 llvm::dbgs () << ((P == TransitivelyPinned) ? " TransitivelyPinned"
97104 : (P == Pinned) ? " Pinned"
98105 : (P == NotPinned) ? " NotPinned"
106+ : (P == Moved) ? " Moved"
99107 : " Error" );
100108 llvm::dbgs () << " ," ;
101109 if (S == Rooted)
@@ -193,8 +201,13 @@ class GCChecker
193201 VS.FD = FD;
194202 return VS;
195203 }
196- // Assume arguments are pinned
197- return getRooted (nullptr , ValueState::Pinned, -1 );
204+ bool require_tpin = declHasAnnotation (PVD, " julia_require_tpin" );
205+ if (require_tpin) {
206+ return getRooted (nullptr , ValueState::TransitivelyPinned, -1 );
207+ } else {
208+ // Assume arguments are pinned
209+ return getRooted (nullptr , ValueState::Pinned, -1 );
210+ }
198211 }
199212 };
200213
@@ -230,7 +243,6 @@ class GCChecker
230243 : " Error" );
231244 llvm::dbgs () << " ,Depth=" ;
232245 llvm::dbgs () << RootedAtDepth;
233- llvm::dbgs () << " \n " ;
234246 }
235247 };
236248
@@ -325,6 +337,7 @@ class GCChecker
325337 SymbolRef getSymbolForResult (const Expr *Result, const ValueState *OldValS,
326338 ProgramStateRef &State, CheckerContext &C) const ;
327339 void validateValue (const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message) const ;
340+ void validateValueRootnessOnly (const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message) const ;
328341 void validateValue (const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message, SourceRange range) const ;
329342 int validateValueInner (const GCChecker::ValueState* VS) const ;
330343 GCChecker::ValueState getRootedFromRegion (const MemRegion *Region, const PinState *PS, int Depth) const ;
@@ -401,13 +414,16 @@ SymbolRef GCChecker::walkToRoot(callback f, const ProgramStateRef &State,
401414 const MemRegion *Region) {
402415 if (!Region)
403416 return nullptr ;
417+ logWithDump (" - walkToRoot, Region" , Region);
404418 while (true ) {
405419 const SymbolicRegion *SR = Region->getSymbolicBase ();
406420 if (!SR) {
407421 return nullptr ;
408422 }
409423 SymbolRef Sym = SR->getSymbol ();
410424 const ValueState *OldVState = State->get <GCValueMap>(Sym);
425+ logWithDump (" - walkToRoot, Sym" , Sym);
426+ logWithDump (" - walkToRoot, OldVState" , OldVState);
411427 if (f (Sym, OldVState)) {
412428 if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(Sym)) {
413429 Region = SRV->getRegion ();
@@ -468,6 +484,15 @@ void GCChecker::validateValue(const ValueState* VS, CheckerContext &C, SymbolRef
468484 }
469485}
470486
487+ void GCChecker::validateValueRootnessOnly (const ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message) const {
488+ int v = validateValueInner (VS);
489+ if (v == FREED) {
490+ GCChecker::report_value_error (C, Sym, (std::string (message) + " GCed" ).c_str ());
491+ } else if (v == MOVED) {
492+ // We don't care if it is moved
493+ }
494+ }
495+
471496void GCChecker::validateValue (const ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message) const {
472497 int v = validateValueInner (VS);
473498 if (v == FREED) {
@@ -717,6 +742,8 @@ PDP GCChecker::GCValueBugVisitor::VisitNode(const ExplodedNode *N,
717742 return MakePDP (Pos, " Root was released here." );
718743 } else if (NewValueState->RootDepth != OldValueState->RootDepth ) {
719744 return MakePDP (Pos, " Rooting Depth changed here." );
745+ } else if (NewValueState->isMoved () && !OldValueState->isMoved ()) {
746+ return MakePDP (Pos, " Value was moved here." );
720747 }
721748 return nullptr ;
722749}
@@ -833,6 +860,7 @@ void GCChecker::checkBeginFunction(CheckerContext &C) const {
833860 const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl ());
834861 if (!FD)
835862 return ;
863+ logWithDump (" checkBeginFunction" , FD);
836864 ProgramStateRef State = C.getState ();
837865 bool Change = false ;
838866 if (C.inTopFrame ()) {
@@ -861,7 +889,10 @@ void GCChecker::checkBeginFunction(CheckerContext &C) const {
861889 auto Param = State->getLValue (P, LCtx);
862890 const MemRegion *Root = State->getSVal (Param).getAsRegion ();
863891 State = State->set <GCRootMap>(Root, RootState::getRoot (-1 ));
864- State = State->set <GCPinMap>(Root, PinState::getPin (-1 ));
892+ if (declHasAnnotation (P, " julia_require_tpin" ))
893+ State = State->set <GCPinMap>(Root, PinState::getTransitivePin (-1 ));
894+ else
895+ State = State->set <GCPinMap>(Root, PinState::getTransitivePin (-1 ));
865896 } else if (isGCTrackedType (P->getType ())) {
866897 auto Param = State->getLValue (P, LCtx);
867898 SymbolRef AssignedSym = State->getSVal (Param).getAsSymbol ();
@@ -880,6 +911,7 @@ void GCChecker::checkBeginFunction(CheckerContext &C) const {
880911
881912void GCChecker::checkEndFunction (const clang::ReturnStmt *RS,
882913 CheckerContext &C) const {
914+ log (" checkEndFunction" );
883915 ProgramStateRef State = C.getState ();
884916
885917 if (RS && gcEnabledHere (C) && RS->getRetValue () && isGCTracked (RS->getRetValue ())) {
@@ -1104,7 +1136,11 @@ bool GCChecker::processPotentialSafepoint(const CallEvent &Call,
11041136 if (RetSym == I.getKey ())
11051137 continue ;
11061138 if (I.getData ().isNotPinned ()) {
1107- State = State->set <GCValueMap>(I.getKey (), ValueState::getMoved (I.getData ()));
1139+ logWithDump (" - move unpinned values, Sym" , I.getKey ());
1140+ logWithDump (" - move unpinned values, VS" , I.getData ());
1141+ auto NewVS = ValueState::getMoved (I.getData ());
1142+ State = State->set <GCValueMap>(I.getKey (), NewVS);
1143+ logWithDump (" - move unpinned values, NewVS" , NewVS);
11081144 DidChange = true ;
11091145 }
11101146 }
@@ -1153,7 +1189,7 @@ bool GCChecker::processArgumentRooting(const CallEvent &Call, CheckerContext &C,
11531189 const ValueState *CurrentVState = State->get <GCValueMap>(RootedSymbol);
11541190 ValueState NewVState = *OldVState;
11551191 // If the old state is pinned, the new state is not pinned.
1156- if (OldVState->isPinned () && ((CurrentVState && CurrentVState->isPinnedByAnyway ()) || !CurrentVState)) {
1192+ if (OldVState->isPinned () && ((CurrentVState && ! CurrentVState->isPinnedByAnyway ()) || !CurrentVState)) {
11571193 NewVState = ValueState::getNotPinned (*OldVState);
11581194 }
11591195 logWithDump (" - Rooted set to" , NewVState);
@@ -1176,6 +1212,8 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call,
11761212 Call.getOriginExpr (), C.getLocationContext (), QT, C.blockCount ());
11771213 State = State->BindExpr (Call.getOriginExpr (), C.getLocationContext (), S);
11781214 Sym = S.getAsSymbol ();
1215+ logWithDump (" - conjureSymbolVal, S" , S);
1216+ logWithDump (" - conjureSymbolVal, Sym" , Sym);
11791217 }
11801218 if (isGloballyRootedType (QT))
11811219 State = State->set <GCValueMap>(Sym, ValueState::getRooted (nullptr , ValueState::pinState (isGloballyTransitivelyPinnedType (QT)), -1 ));
@@ -1229,8 +1267,11 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call,
12291267 const MemRegion *Region = Test.getAsRegion ();
12301268 const ValueState *OldVState =
12311269 getValStateForRegion (C.getASTContext (), State, Region);
1232- if (OldVState)
1233- NewVState = *OldVState;
1270+ if (OldVState) {
1271+ NewVState = ValueState::inheritState (*OldVState);
1272+ logWithDump (" - jl_propagates_root, OldVState" , *OldVState);
1273+ logWithDump (" - jl_propagates_root, NewVState" , NewVState);
1274+ }
12341275 break ;
12351276 }
12361277 }
@@ -1245,8 +1286,11 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call,
12451286void GCChecker::checkPostCall (const CallEvent &Call, CheckerContext &C) const {
12461287 logWithDump (" checkPostCall" , Call);
12471288 ProgramStateRef State = C.getState ();
1289+ log (" - processArgmentRooting" );
12481290 bool didChange = processArgumentRooting (Call, C, State);
1291+ log (" - processPotentialsafepoint" );
12491292 didChange |= processPotentialSafepoint (Call, C, State);
1293+ log (" - processAllocationOfResult" );
12501294 didChange |= processAllocationOfResult (Call, C, State);
12511295 if (didChange)
12521296 C.addTransition (State);
@@ -1255,6 +1299,7 @@ void GCChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
12551299// Implicitly root values that were casted to globally rooted values
12561300void GCChecker::checkPostStmt (const CStyleCastExpr *CE,
12571301 CheckerContext &C) const {
1302+ logWithDump (" checkpostStmt(CStyleCastExpr)" , CE);
12581303 if (!isGloballyRootedType (CE->getTypeAsWritten ()))
12591304 return ;
12601305 SymbolRef Sym = C.getSVal (CE).getAsSymbol ();
@@ -1354,6 +1399,7 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent,
13541399 SymbolRef OldSym = ParentVal.getAsSymbol (true );
13551400 const MemRegion *Region = C.getSVal (Parent).getAsRegion ();
13561401 const ValueState *OldValS = OldSym ? State->get <GCValueMap>(OldSym) : nullptr ;
1402+ logWithDump (" - Region" , Region);
13571403 logWithDump (" - OldSym" , OldSym);
13581404 logWithDump (" - OldValS" , OldValS);
13591405 SymbolRef NewSym = getSymbolForResult (Result, OldValS, State, C);
@@ -1363,6 +1409,7 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent,
13631409 logWithDump (" - NewSym" , NewSym);
13641410 // NewSym might already have a better root
13651411 const ValueState *NewValS = State->get <GCValueMap>(NewSym);
1412+ logWithDump (" - NewValS" , NewValS);
13661413 if (Region) {
13671414 const VarRegion *VR = Region->getAs <VarRegion>();
13681415 bool inheritedState = false ;
@@ -1412,8 +1459,9 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent,
14121459 validateValue (OldValS, C, OldSym, " Creating derivative of value that may have been" );
14131460 if (!OldValS->isPotentiallyFreed () && ResultTracked) {
14141461 logWithDump (" - Set as OldValS, Sym" , NewSym);
1415- logWithDump (" - Set as OldValS, VS" , OldValS);
1416- C.addTransition (State->set <GCValueMap>(NewSym, *OldValS));
1462+ auto InheritVS = ValueState::inheritState (*OldValS);
1463+ logWithDump (" - Set as OldValS, InheritVS" , InheritVS);
1464+ C.addTransition (State->set <GCValueMap>(NewSym, InheritVS));
14171465 return ;
14181466 }
14191467}
@@ -1481,6 +1529,7 @@ void GCChecker::checkPostStmt(const MemberExpr *ME, CheckerContext &C) const {
14811529
14821530void GCChecker::checkPostStmt (const UnaryOperator *UO,
14831531 CheckerContext &C) const {
1532+ logWithDump (" checkPostStmt(UnaryOperator)" , UO);
14841533 if (UO->getOpcode () == UO_Deref) {
14851534 checkDerivingExpr (UO, UO->getSubExpr (), true , C);
14861535 }
@@ -1590,6 +1639,11 @@ void GCChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
15901639 report_value_error (C, Sym, " Passing non-pinned value as argument to function that may GC" , range);
15911640 }
15921641 }
1642+ if (FD && idx < FD->getNumParams () && declHasAnnotation (FD->getParamDecl (idx), " julia_require_tpin" )) {
1643+ if (!ValState->isTransitivelyPinned ()) {
1644+ report_value_error (C, Sym, " Passing non-tpinned argument to function that requires a tpin argument." );
1645+ }
1646+ }
15931647 }
15941648}
15951649
@@ -1732,6 +1786,13 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
17321786 report_error (C, " Can not understand this pin." );
17331787 return true ;
17341788 }
1789+
1790+ const ValueState *OldVS = C.getState ()->get <GCValueMap>(Sym);
1791+ if (OldVS && OldVS->isMoved ()) {
1792+ report_error (C, " Attempt to PIN a value that is already moved." );
1793+ return true ;
1794+ }
1795+
17351796 auto MRV = Arg.getAs <loc::MemRegionVal>();
17361797 if (!MRV) {
17371798 report_error (C, " PTR_PIN with something other than a local variable" );
@@ -1740,7 +1801,6 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
17401801 const MemRegion *Region = MRV->getRegion ();
17411802 auto State = C.getState ()->set <GCPinMap>(Region, PinState::getPin (CurrentDepth));
17421803 logWithDump (" - Pin region" , Region);
1743- const ValueState *OldVS = State->get <GCValueMap>(Sym);
17441804 State = State->set <GCValueMap>(Sym, ValueState::getPinned (*OldVS));
17451805 logWithDump (" - Pin value" , Sym);
17461806 C.addTransition (State);
@@ -1885,16 +1945,21 @@ void GCChecker::checkBind(SVal LVal, SVal RVal, const clang::Stmt *S,
18851945 log (" - No Sym" );
18861946 return ;
18871947 }
1948+ logWithDump (" - Sym" , Sym);
18881949 const auto *RootState = State->get <GCRootMap>(R);
18891950 logWithDump (" - R" , R);
18901951 logWithDump (" - RootState for R" , RootState);
18911952 if (!RootState) {
18921953 const ValueState *ValSP = nullptr ;
18931954 ValueState ValS;
18941955 if (rootRegionIfGlobal (R->getBaseRegion (), State, C, &ValS)) {
1956+ logWithDump (" - rootRegionIfGlobal, base" , R->getBaseRegion ());
18951957 ValSP = &ValS;
1958+ logWithDump (" - rootRegionIfGlobal ValSP" , ValSP);
18961959 } else {
1960+ logWithDump (" - getValStateForRegion" , R);
18971961 ValSP = getValStateForRegion (C.getASTContext (), State, R);
1962+ logWithDump (" - getValStateForRegion" , ValSP);
18981963 }
18991964 if (ValSP && ValSP->isRooted ()) {
19001965 logWithDump (" - Found base region that is rooted" , ValSP);
@@ -1903,8 +1968,9 @@ void GCChecker::checkBind(SVal LVal, SVal RVal, const clang::Stmt *S,
19031968 RValState->RootDepth < ValSP->RootDepth ) {
19041969 logWithDump (" - No need to set ValState, current ValState" , RValState);
19051970 } else {
1906- logWithDump (" - Set ValState, current ValState" , ValSP);
1907- C.addTransition (State->set <GCValueMap>(Sym, *ValSP));
1971+ auto InheritVS = ValueState::inheritState (*ValSP);
1972+ logWithDump (" - Set ValState, InheritVS" , InheritVS);
1973+ C.addTransition (State->set <GCValueMap>(Sym, InheritVS));
19081974 }
19091975 }
19101976 } else {
@@ -1929,7 +1995,7 @@ void GCChecker::checkBind(SVal LVal, SVal RVal, const clang::Stmt *S,
19291995 } else {
19301996 logWithDump (" - Found ValState for Sym" , RValState);
19311997 validateValue (RValState, C, Sym, " Trying to root value which may have been" );
1932- if (!RValState->isRooted () ||
1998+ if (!RValState->isRooted () || !RValState-> isPinnedByAnyway () ||
19331999 RValState->RootDepth > RootState->RootedAtDepth ) {
19342000 auto NewVS = getRootedFromRegion (R, State->get <GCPinMap>(R), RootState->RootedAtDepth );
19352001 logWithDump (" - getRootedFromRegion" , NewVS);
@@ -1994,48 +2060,62 @@ bool GCChecker::rootRegionIfGlobal(const MemRegion *R, ProgramStateRef &State,
19942060
19952061void GCChecker::checkLocation (SVal SLoc, bool IsLoad, const Stmt *S,
19962062 CheckerContext &C) const {
2063+ logWithDump (" checkLocation" , SLoc);
19972064 ProgramStateRef State = C.getState ();
19982065 bool DidChange = false ;
19992066 const RootState *RS = nullptr ;
20002067 // Loading from a root produces a rooted symbol. TODO: Can we do something
20012068 // better than this.
20022069 if (IsLoad && (RS = State->get <GCRootMap>(SLoc.getAsRegion ()))) {
2070+ logWithDump (" - IsLoad, RS" , RS);
20032071 SymbolRef LoadedSym =
20042072 State->getSVal (SLoc.getAs <Loc>().getValue ()).getAsSymbol ();
20052073 if (LoadedSym) {
20062074 const ValueState *ValS = State->get <GCValueMap>(LoadedSym);
2007- if (!ValS || !ValS->isRooted () || ValS->RootDepth > RS->RootedAtDepth ) {
2075+ logWithDump (" - IsLoad, LoadedSym" , LoadedSym);
2076+ logWithDump (" - IsLoad, ValS" , ValS);
2077+ if (!ValS || !ValS->isRooted () || !ValS->isPinnedByAnyway () || ValS->RootDepth > RS->RootedAtDepth ) {
2078+ auto NewVS = getRootedFromRegion (SLoc.getAsRegion (), State->get <GCPinMap>(SLoc.getAsRegion ()), RS->RootedAtDepth );
2079+ logWithDump (" - IsLoad, NewVS" , NewVS);
20082080 DidChange = true ;
2009- State = State->set <GCValueMap>(
2010- LoadedSym,
2011- getRootedFromRegion (SLoc.getAsRegion (), State->get <GCPinMap>(SLoc.getAsRegion ()), RS->RootedAtDepth ));
2081+ State = State->set <GCValueMap>(LoadedSym, NewVS);
20122082 }
20132083 }
20142084 }
2085+ logWithDump (" - getAsRegion()" , SLoc.getAsRegion ());
20152086 // If it's just the symbol by itself, let it be. We allow dead pointer to be
20162087 // passed around, so long as they're not accessed. However, we do want to
20172088 // start tracking any globals that may have been accessed.
20182089 if (rootRegionIfGlobal (SLoc.getAsRegion (), State, C)) {
20192090 C.addTransition (State);
2091+ log (" - rootRegionIfGlobal" );
20202092 return ;
20212093 }
20222094 SymbolRef SymByItself = SLoc.getAsSymbol (false );
2095+ logWithDump (" - SymByItself" , SymByItself);
20232096 if (SymByItself) {
20242097 DidChange &&C.addTransition (State);
20252098 return ;
20262099 }
20272100 // This will walk backwards until it finds the base symbol
20282101 SymbolRef Sym = SLoc.getAsSymbol (true );
2102+ logWithDump (" - Sym" , Sym);
20292103 if (!Sym) {
20302104 DidChange &&C.addTransition (State);
20312105 return ;
20322106 }
20332107 const ValueState *VState = State->get <GCValueMap>(Sym);
2108+ logWithDump (" - VState" , VState);
20342109 if (!VState) {
20352110 DidChange &&C.addTransition (State);
20362111 return ;
20372112 }
2038- validateValue (VState, C, Sym, " Trying to access value which may have been" );
2113+ // If this is the sym, we verify both rootness and pinning. Otherwise, it may be the parent sym and we only care about the rootness.
2114+ if (SymByItself == Sym) {
2115+ validateValue (VState, C, Sym, " Trying to access value which may have been" );
2116+ } else {
2117+ validateValueRootnessOnly (VState, C, Sym, " Trying to access value which may have been" );
2118+ }
20392119 DidChange &&C.addTransition (State);
20402120}
20412121
0 commit comments