3
3
// Assumptions for pinning:
4
4
// * args need to be pinned
5
5
// * 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.
6
13
7
14
#include " clang/Frontend/FrontendActions.h"
8
15
#include " clang/StaticAnalyzer/Checkers/SValExplainer.h"
@@ -96,6 +103,7 @@ class GCChecker
96
103
llvm::dbgs () << ((P == TransitivelyPinned) ? " TransitivelyPinned"
97
104
: (P == Pinned) ? " Pinned"
98
105
: (P == NotPinned) ? " NotPinned"
106
+ : (P == Moved) ? " Moved"
99
107
: " Error" );
100
108
llvm::dbgs () << " ," ;
101
109
if (S == Rooted)
@@ -193,8 +201,13 @@ class GCChecker
193
201
VS.FD = FD;
194
202
return VS;
195
203
}
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
+ }
198
211
}
199
212
};
200
213
@@ -230,7 +243,6 @@ class GCChecker
230
243
: " Error" );
231
244
llvm::dbgs () << " ,Depth=" ;
232
245
llvm::dbgs () << RootedAtDepth;
233
- llvm::dbgs () << " \n " ;
234
246
}
235
247
};
236
248
@@ -325,6 +337,7 @@ class GCChecker
325
337
SymbolRef getSymbolForResult (const Expr *Result, const ValueState *OldValS,
326
338
ProgramStateRef &State, CheckerContext &C) const ;
327
339
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 ;
328
341
void validateValue (const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message, SourceRange range) const ;
329
342
int validateValueInner (const GCChecker::ValueState* VS) const ;
330
343
GCChecker::ValueState getRootedFromRegion (const MemRegion *Region, const PinState *PS, int Depth) const ;
@@ -401,13 +414,16 @@ SymbolRef GCChecker::walkToRoot(callback f, const ProgramStateRef &State,
401
414
const MemRegion *Region) {
402
415
if (!Region)
403
416
return nullptr ;
417
+ logWithDump (" - walkToRoot, Region" , Region);
404
418
while (true ) {
405
419
const SymbolicRegion *SR = Region->getSymbolicBase ();
406
420
if (!SR) {
407
421
return nullptr ;
408
422
}
409
423
SymbolRef Sym = SR->getSymbol ();
410
424
const ValueState *OldVState = State->get <GCValueMap>(Sym);
425
+ logWithDump (" - walkToRoot, Sym" , Sym);
426
+ logWithDump (" - walkToRoot, OldVState" , OldVState);
411
427
if (f (Sym, OldVState)) {
412
428
if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(Sym)) {
413
429
Region = SRV->getRegion ();
@@ -468,6 +484,15 @@ void GCChecker::validateValue(const ValueState* VS, CheckerContext &C, SymbolRef
468
484
}
469
485
}
470
486
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
+
471
496
void GCChecker::validateValue (const ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message) const {
472
497
int v = validateValueInner (VS);
473
498
if (v == FREED) {
@@ -717,6 +742,8 @@ PDP GCChecker::GCValueBugVisitor::VisitNode(const ExplodedNode *N,
717
742
return MakePDP (Pos, " Root was released here." );
718
743
} else if (NewValueState->RootDepth != OldValueState->RootDepth ) {
719
744
return MakePDP (Pos, " Rooting Depth changed here." );
745
+ } else if (NewValueState->isMoved () && !OldValueState->isMoved ()) {
746
+ return MakePDP (Pos, " Value was moved here." );
720
747
}
721
748
return nullptr ;
722
749
}
@@ -833,6 +860,7 @@ void GCChecker::checkBeginFunction(CheckerContext &C) const {
833
860
const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl ());
834
861
if (!FD)
835
862
return ;
863
+ logWithDump (" checkBeginFunction" , FD);
836
864
ProgramStateRef State = C.getState ();
837
865
bool Change = false ;
838
866
if (C.inTopFrame ()) {
@@ -861,7 +889,10 @@ void GCChecker::checkBeginFunction(CheckerContext &C) const {
861
889
auto Param = State->getLValue (P, LCtx);
862
890
const MemRegion *Root = State->getSVal (Param).getAsRegion ();
863
891
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 ));
865
896
} else if (isGCTrackedType (P->getType ())) {
866
897
auto Param = State->getLValue (P, LCtx);
867
898
SymbolRef AssignedSym = State->getSVal (Param).getAsSymbol ();
@@ -880,6 +911,7 @@ void GCChecker::checkBeginFunction(CheckerContext &C) const {
880
911
881
912
void GCChecker::checkEndFunction (const clang::ReturnStmt *RS,
882
913
CheckerContext &C) const {
914
+ log (" checkEndFunction" );
883
915
ProgramStateRef State = C.getState ();
884
916
885
917
if (RS && gcEnabledHere (C) && RS->getRetValue () && isGCTracked (RS->getRetValue ())) {
@@ -1104,7 +1136,11 @@ bool GCChecker::processPotentialSafepoint(const CallEvent &Call,
1104
1136
if (RetSym == I.getKey ())
1105
1137
continue ;
1106
1138
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);
1108
1144
DidChange = true ;
1109
1145
}
1110
1146
}
@@ -1153,7 +1189,7 @@ bool GCChecker::processArgumentRooting(const CallEvent &Call, CheckerContext &C,
1153
1189
const ValueState *CurrentVState = State->get <GCValueMap>(RootedSymbol);
1154
1190
ValueState NewVState = *OldVState;
1155
1191
// 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)) {
1157
1193
NewVState = ValueState::getNotPinned (*OldVState);
1158
1194
}
1159
1195
logWithDump (" - Rooted set to" , NewVState);
@@ -1176,6 +1212,8 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call,
1176
1212
Call.getOriginExpr (), C.getLocationContext (), QT, C.blockCount ());
1177
1213
State = State->BindExpr (Call.getOriginExpr (), C.getLocationContext (), S);
1178
1214
Sym = S.getAsSymbol ();
1215
+ logWithDump (" - conjureSymbolVal, S" , S);
1216
+ logWithDump (" - conjureSymbolVal, Sym" , Sym);
1179
1217
}
1180
1218
if (isGloballyRootedType (QT))
1181
1219
State = State->set <GCValueMap>(Sym, ValueState::getRooted (nullptr , ValueState::pinState (isGloballyTransitivelyPinnedType (QT)), -1 ));
@@ -1229,8 +1267,11 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call,
1229
1267
const MemRegion *Region = Test.getAsRegion ();
1230
1268
const ValueState *OldVState =
1231
1269
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
+ }
1234
1275
break ;
1235
1276
}
1236
1277
}
@@ -1245,8 +1286,11 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call,
1245
1286
void GCChecker::checkPostCall (const CallEvent &Call, CheckerContext &C) const {
1246
1287
logWithDump (" checkPostCall" , Call);
1247
1288
ProgramStateRef State = C.getState ();
1289
+ log (" - processArgmentRooting" );
1248
1290
bool didChange = processArgumentRooting (Call, C, State);
1291
+ log (" - processPotentialsafepoint" );
1249
1292
didChange |= processPotentialSafepoint (Call, C, State);
1293
+ log (" - processAllocationOfResult" );
1250
1294
didChange |= processAllocationOfResult (Call, C, State);
1251
1295
if (didChange)
1252
1296
C.addTransition (State);
@@ -1255,6 +1299,7 @@ void GCChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
1255
1299
// Implicitly root values that were casted to globally rooted values
1256
1300
void GCChecker::checkPostStmt (const CStyleCastExpr *CE,
1257
1301
CheckerContext &C) const {
1302
+ logWithDump (" checkpostStmt(CStyleCastExpr)" , CE);
1258
1303
if (!isGloballyRootedType (CE->getTypeAsWritten ()))
1259
1304
return ;
1260
1305
SymbolRef Sym = C.getSVal (CE).getAsSymbol ();
@@ -1354,6 +1399,7 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent,
1354
1399
SymbolRef OldSym = ParentVal.getAsSymbol (true );
1355
1400
const MemRegion *Region = C.getSVal (Parent).getAsRegion ();
1356
1401
const ValueState *OldValS = OldSym ? State->get <GCValueMap>(OldSym) : nullptr ;
1402
+ logWithDump (" - Region" , Region);
1357
1403
logWithDump (" - OldSym" , OldSym);
1358
1404
logWithDump (" - OldValS" , OldValS);
1359
1405
SymbolRef NewSym = getSymbolForResult (Result, OldValS, State, C);
@@ -1363,6 +1409,7 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent,
1363
1409
logWithDump (" - NewSym" , NewSym);
1364
1410
// NewSym might already have a better root
1365
1411
const ValueState *NewValS = State->get <GCValueMap>(NewSym);
1412
+ logWithDump (" - NewValS" , NewValS);
1366
1413
if (Region) {
1367
1414
const VarRegion *VR = Region->getAs <VarRegion>();
1368
1415
bool inheritedState = false ;
@@ -1412,8 +1459,9 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent,
1412
1459
validateValue (OldValS, C, OldSym, " Creating derivative of value that may have been" );
1413
1460
if (!OldValS->isPotentiallyFreed () && ResultTracked) {
1414
1461
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));
1417
1465
return ;
1418
1466
}
1419
1467
}
@@ -1481,6 +1529,7 @@ void GCChecker::checkPostStmt(const MemberExpr *ME, CheckerContext &C) const {
1481
1529
1482
1530
void GCChecker::checkPostStmt (const UnaryOperator *UO,
1483
1531
CheckerContext &C) const {
1532
+ logWithDump (" checkPostStmt(UnaryOperator)" , UO);
1484
1533
if (UO->getOpcode () == UO_Deref) {
1485
1534
checkDerivingExpr (UO, UO->getSubExpr (), true , C);
1486
1535
}
@@ -1590,6 +1639,11 @@ void GCChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
1590
1639
report_value_error (C, Sym, " Passing non-pinned value as argument to function that may GC" , range);
1591
1640
}
1592
1641
}
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
+ }
1593
1647
}
1594
1648
}
1595
1649
@@ -1732,6 +1786,13 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
1732
1786
report_error (C, " Can not understand this pin." );
1733
1787
return true ;
1734
1788
}
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
+
1735
1796
auto MRV = Arg.getAs <loc::MemRegionVal>();
1736
1797
if (!MRV) {
1737
1798
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 {
1740
1801
const MemRegion *Region = MRV->getRegion ();
1741
1802
auto State = C.getState ()->set <GCPinMap>(Region, PinState::getPin (CurrentDepth));
1742
1803
logWithDump (" - Pin region" , Region);
1743
- const ValueState *OldVS = State->get <GCValueMap>(Sym);
1744
1804
State = State->set <GCValueMap>(Sym, ValueState::getPinned (*OldVS));
1745
1805
logWithDump (" - Pin value" , Sym);
1746
1806
C.addTransition (State);
@@ -1885,16 +1945,21 @@ void GCChecker::checkBind(SVal LVal, SVal RVal, const clang::Stmt *S,
1885
1945
log (" - No Sym" );
1886
1946
return ;
1887
1947
}
1948
+ logWithDump (" - Sym" , Sym);
1888
1949
const auto *RootState = State->get <GCRootMap>(R);
1889
1950
logWithDump (" - R" , R);
1890
1951
logWithDump (" - RootState for R" , RootState);
1891
1952
if (!RootState) {
1892
1953
const ValueState *ValSP = nullptr ;
1893
1954
ValueState ValS;
1894
1955
if (rootRegionIfGlobal (R->getBaseRegion (), State, C, &ValS)) {
1956
+ logWithDump (" - rootRegionIfGlobal, base" , R->getBaseRegion ());
1895
1957
ValSP = &ValS;
1958
+ logWithDump (" - rootRegionIfGlobal ValSP" , ValSP);
1896
1959
} else {
1960
+ logWithDump (" - getValStateForRegion" , R);
1897
1961
ValSP = getValStateForRegion (C.getASTContext (), State, R);
1962
+ logWithDump (" - getValStateForRegion" , ValSP);
1898
1963
}
1899
1964
if (ValSP && ValSP->isRooted ()) {
1900
1965
logWithDump (" - Found base region that is rooted" , ValSP);
@@ -1903,8 +1968,9 @@ void GCChecker::checkBind(SVal LVal, SVal RVal, const clang::Stmt *S,
1903
1968
RValState->RootDepth < ValSP->RootDepth ) {
1904
1969
logWithDump (" - No need to set ValState, current ValState" , RValState);
1905
1970
} 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));
1908
1974
}
1909
1975
}
1910
1976
} else {
@@ -1929,7 +1995,7 @@ void GCChecker::checkBind(SVal LVal, SVal RVal, const clang::Stmt *S,
1929
1995
} else {
1930
1996
logWithDump (" - Found ValState for Sym" , RValState);
1931
1997
validateValue (RValState, C, Sym, " Trying to root value which may have been" );
1932
- if (!RValState->isRooted () ||
1998
+ if (!RValState->isRooted () || !RValState-> isPinnedByAnyway () ||
1933
1999
RValState->RootDepth > RootState->RootedAtDepth ) {
1934
2000
auto NewVS = getRootedFromRegion (R, State->get <GCPinMap>(R), RootState->RootedAtDepth );
1935
2001
logWithDump (" - getRootedFromRegion" , NewVS);
@@ -1994,48 +2060,62 @@ bool GCChecker::rootRegionIfGlobal(const MemRegion *R, ProgramStateRef &State,
1994
2060
1995
2061
void GCChecker::checkLocation (SVal SLoc, bool IsLoad, const Stmt *S,
1996
2062
CheckerContext &C) const {
2063
+ logWithDump (" checkLocation" , SLoc);
1997
2064
ProgramStateRef State = C.getState ();
1998
2065
bool DidChange = false ;
1999
2066
const RootState *RS = nullptr ;
2000
2067
// Loading from a root produces a rooted symbol. TODO: Can we do something
2001
2068
// better than this.
2002
2069
if (IsLoad && (RS = State->get <GCRootMap>(SLoc.getAsRegion ()))) {
2070
+ logWithDump (" - IsLoad, RS" , RS);
2003
2071
SymbolRef LoadedSym =
2004
2072
State->getSVal (SLoc.getAs <Loc>().getValue ()).getAsSymbol ();
2005
2073
if (LoadedSym) {
2006
2074
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);
2008
2080
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);
2012
2082
}
2013
2083
}
2014
2084
}
2085
+ logWithDump (" - getAsRegion()" , SLoc.getAsRegion ());
2015
2086
// If it's just the symbol by itself, let it be. We allow dead pointer to be
2016
2087
// passed around, so long as they're not accessed. However, we do want to
2017
2088
// start tracking any globals that may have been accessed.
2018
2089
if (rootRegionIfGlobal (SLoc.getAsRegion (), State, C)) {
2019
2090
C.addTransition (State);
2091
+ log (" - rootRegionIfGlobal" );
2020
2092
return ;
2021
2093
}
2022
2094
SymbolRef SymByItself = SLoc.getAsSymbol (false );
2095
+ logWithDump (" - SymByItself" , SymByItself);
2023
2096
if (SymByItself) {
2024
2097
DidChange &&C.addTransition (State);
2025
2098
return ;
2026
2099
}
2027
2100
// This will walk backwards until it finds the base symbol
2028
2101
SymbolRef Sym = SLoc.getAsSymbol (true );
2102
+ logWithDump (" - Sym" , Sym);
2029
2103
if (!Sym) {
2030
2104
DidChange &&C.addTransition (State);
2031
2105
return ;
2032
2106
}
2033
2107
const ValueState *VState = State->get <GCValueMap>(Sym);
2108
+ logWithDump (" - VState" , VState);
2034
2109
if (!VState) {
2035
2110
DidChange &&C.addTransition (State);
2036
2111
return ;
2037
2112
}
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
+ }
2039
2119
DidChange &&C.addTransition (State);
2040
2120
}
2041
2121
0 commit comments