@@ -249,6 +249,10 @@ struct StreamOperationEvaluator {
249
249
250
250
bool isStreamEof () const { return SS->ErrorState == ErrorFEof; }
251
251
252
+ NonLoc getZeroVal (const CallEvent &Call) {
253
+ return *SVB.makeZeroVal (Call.getResultType ()).getAs <NonLoc>();
254
+ }
255
+
252
256
ProgramStateRef setStreamState (ProgramStateRef State,
253
257
const StreamState &NewSS) {
254
258
return State->set <StreamMap>(StreamSym, NewSS);
@@ -390,7 +394,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
390
394
{&StreamChecker::preDefault,
391
395
std::bind (&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
392
396
0 }},
393
- {{{" fileno" }, 1 }, {&StreamChecker::preDefault, nullptr , 0 }},
397
+ {{{" fileno" }, 1 },
398
+ {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0 }},
394
399
};
395
400
396
401
CallDescriptionMap<FnDescription> FnTestDescriptions = {
@@ -486,6 +491,9 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
486
491
void evalFflush (const FnDescription *Desc, const CallEvent &Call,
487
492
CheckerContext &C) const ;
488
493
494
+ void evalFileno (const FnDescription *Desc, const CallEvent &Call,
495
+ CheckerContext &C) const ;
496
+
489
497
// / Check that the stream (in StreamVal) is not NULL.
490
498
// / If it can only be NULL a fatal error is emitted and nullptr returned.
491
499
// / Otherwise the return value is a new state where the stream is constrained
@@ -929,8 +937,7 @@ void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
929
937
ProgramStateRef StateNotFailed =
930
938
State->BindExpr (E.CE , C.getLocationContext (), RetVal);
931
939
StateNotFailed =
932
- E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal,
933
- *E.SVB .makeZeroVal (E.ACtx .IntTy ).getAs <NonLoc>());
940
+ E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal, E.getZeroVal (Call));
934
941
if (!StateNotFailed)
935
942
return ;
936
943
StateNotFailed =
@@ -1003,8 +1010,7 @@ void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
1003
1010
ProgramStateRef StateNotFailed =
1004
1011
State->BindExpr (E.CE , C.getLocationContext (), RetVal);
1005
1012
StateNotFailed =
1006
- E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal,
1007
- *E.SVB .makeZeroVal (E.ACtx .IntTy ).getAs <NonLoc>());
1013
+ E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal, E.getZeroVal (Call));
1008
1014
if (StateNotFailed)
1009
1015
C.addTransition (StateNotFailed);
1010
1016
}
@@ -1073,8 +1079,7 @@ void StreamChecker::evalGetdelim(const FnDescription *Desc,
1073
1079
ProgramStateRef StateNotFailed =
1074
1080
State->BindExpr (E.CE , C.getLocationContext (), RetVal);
1075
1081
StateNotFailed =
1076
- E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal,
1077
- *E.SVB .makeZeroVal (E.CE ->getType ()).getAs <NonLoc>());
1082
+ E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal, E.getZeroVal (Call));
1078
1083
if (!StateNotFailed)
1079
1084
return ;
1080
1085
C.addTransition (StateNotFailed);
@@ -1200,8 +1205,7 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1200
1205
ProgramStateRef StateNotFailed =
1201
1206
State->BindExpr (E.CE , C.getLocationContext (), RetVal);
1202
1207
StateNotFailed =
1203
- E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal,
1204
- *E.SVB .makeZeroVal (Call.getResultType ()).getAs <NonLoc>());
1208
+ E.assumeBinOpNN (StateNotFailed, BO_GE, RetVal, E.getZeroVal (Call));
1205
1209
if (!StateNotFailed)
1206
1210
return ;
1207
1211
@@ -1226,79 +1230,6 @@ void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1226
1230
C.addTransition (State);
1227
1231
}
1228
1232
1229
- void StreamChecker::evalClearerr (const FnDescription *Desc,
1230
- const CallEvent &Call,
1231
- CheckerContext &C) const {
1232
- ProgramStateRef State = C.getState ();
1233
- StreamOperationEvaluator E (C);
1234
- if (!E.Init (Desc, Call, C, State))
1235
- return ;
1236
-
1237
- // FilePositionIndeterminate is not cleared.
1238
- State = E.setStreamState (
1239
- State,
1240
- StreamState::getOpened (Desc, ErrorNone, E.SS ->FilePositionIndeterminate ));
1241
- C.addTransition (State);
1242
- }
1243
-
1244
- void StreamChecker::evalFeofFerror (const FnDescription *Desc,
1245
- const CallEvent &Call, CheckerContext &C,
1246
- const StreamErrorState &ErrorKind) const {
1247
- ProgramStateRef State = C.getState ();
1248
- StreamOperationEvaluator E (C);
1249
- if (!E.Init (Desc, Call, C, State))
1250
- return ;
1251
-
1252
- if (E.SS ->ErrorState & ErrorKind) {
1253
- // Execution path with error of ErrorKind.
1254
- // Function returns true.
1255
- // From now on it is the only one error state.
1256
- ProgramStateRef TrueState = bindAndAssumeTrue (State, C, E.CE );
1257
- C.addTransition (E.setStreamState (
1258
- TrueState, StreamState::getOpened (Desc, ErrorKind,
1259
- E.SS ->FilePositionIndeterminate &&
1260
- !ErrorKind.isFEof ())));
1261
- }
1262
- if (StreamErrorState NewES = E.SS ->ErrorState & (~ErrorKind)) {
1263
- // Execution path(s) with ErrorKind not set.
1264
- // Function returns false.
1265
- // New error state is everything before minus ErrorKind.
1266
- ProgramStateRef FalseState = E.bindReturnValue (State, C, 0 );
1267
- C.addTransition (E.setStreamState (
1268
- FalseState,
1269
- StreamState::getOpened (
1270
- Desc, NewES, E.SS ->FilePositionIndeterminate && !NewES.isFEof ())));
1271
- }
1272
- }
1273
-
1274
- void StreamChecker::preDefault (const FnDescription *Desc, const CallEvent &Call,
1275
- CheckerContext &C) const {
1276
- ProgramStateRef State = C.getState ();
1277
- SVal StreamVal = getStreamArg (Desc, Call);
1278
- State = ensureStreamNonNull (StreamVal, Call.getArgExpr (Desc->StreamArgNo ), C,
1279
- State);
1280
- if (!State)
1281
- return ;
1282
- State = ensureStreamOpened (StreamVal, C, State);
1283
- if (!State)
1284
- return ;
1285
-
1286
- C.addTransition (State);
1287
- }
1288
-
1289
- void StreamChecker::evalSetFeofFerror (const FnDescription *Desc,
1290
- const CallEvent &Call, CheckerContext &C,
1291
- const StreamErrorState &ErrorKind) const {
1292
- ProgramStateRef State = C.getState ();
1293
- SymbolRef StreamSym = getStreamArg (Desc, Call).getAsSymbol ();
1294
- assert (StreamSym && " Operation not permitted on non-symbolic stream value." );
1295
- const StreamState *SS = State->get <StreamMap>(StreamSym);
1296
- assert (SS && " Stream should be tracked by the checker." );
1297
- State = State->set <StreamMap>(
1298
- StreamSym, StreamState::getOpened (SS->LastOperation , ErrorKind));
1299
- C.addTransition (State);
1300
- }
1301
-
1302
1233
void StreamChecker::preFflush (const FnDescription *Desc, const CallEvent &Call,
1303
1234
CheckerContext &C) const {
1304
1235
ProgramStateRef State = C.getState ();
@@ -1377,6 +1308,104 @@ void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
1377
1308
C.addTransition (StateFailed);
1378
1309
}
1379
1310
1311
+ void StreamChecker::evalClearerr (const FnDescription *Desc,
1312
+ const CallEvent &Call,
1313
+ CheckerContext &C) const {
1314
+ ProgramStateRef State = C.getState ();
1315
+ StreamOperationEvaluator E (C);
1316
+ if (!E.Init (Desc, Call, C, State))
1317
+ return ;
1318
+
1319
+ // FilePositionIndeterminate is not cleared.
1320
+ State = E.setStreamState (
1321
+ State,
1322
+ StreamState::getOpened (Desc, ErrorNone, E.SS ->FilePositionIndeterminate ));
1323
+ C.addTransition (State);
1324
+ }
1325
+
1326
+ void StreamChecker::evalFeofFerror (const FnDescription *Desc,
1327
+ const CallEvent &Call, CheckerContext &C,
1328
+ const StreamErrorState &ErrorKind) const {
1329
+ ProgramStateRef State = C.getState ();
1330
+ StreamOperationEvaluator E (C);
1331
+ if (!E.Init (Desc, Call, C, State))
1332
+ return ;
1333
+
1334
+ if (E.SS ->ErrorState & ErrorKind) {
1335
+ // Execution path with error of ErrorKind.
1336
+ // Function returns true.
1337
+ // From now on it is the only one error state.
1338
+ ProgramStateRef TrueState = bindAndAssumeTrue (State, C, E.CE );
1339
+ C.addTransition (E.setStreamState (
1340
+ TrueState, StreamState::getOpened (Desc, ErrorKind,
1341
+ E.SS ->FilePositionIndeterminate &&
1342
+ !ErrorKind.isFEof ())));
1343
+ }
1344
+ if (StreamErrorState NewES = E.SS ->ErrorState & (~ErrorKind)) {
1345
+ // Execution path(s) with ErrorKind not set.
1346
+ // Function returns false.
1347
+ // New error state is everything before minus ErrorKind.
1348
+ ProgramStateRef FalseState = E.bindReturnValue (State, C, 0 );
1349
+ C.addTransition (E.setStreamState (
1350
+ FalseState,
1351
+ StreamState::getOpened (
1352
+ Desc, NewES, E.SS ->FilePositionIndeterminate && !NewES.isFEof ())));
1353
+ }
1354
+ }
1355
+
1356
+ void StreamChecker::evalFileno (const FnDescription *Desc, const CallEvent &Call,
1357
+ CheckerContext &C) const {
1358
+ // Fileno should fail only if the passed pointer is invalid.
1359
+ // Some of the preconditions are checked already in preDefault.
1360
+ // Here we can assume that the operation does not fail, because if we
1361
+ // introduced a separate branch where fileno() returns -1, then it would cause
1362
+ // many unexpected and unwanted warnings in situations where fileno() is
1363
+ // called on valid streams.
1364
+ // The stream error states are not modified by 'fileno', and 'errno' is also
1365
+ // left unchanged (so this evalCall does not invalidate it, but we have a
1366
+ // custom evalCall instead of the default that would invalidate it).
1367
+ ProgramStateRef State = C.getState ();
1368
+ StreamOperationEvaluator E (C);
1369
+ if (!E.Init (Desc, Call, C, State))
1370
+ return ;
1371
+
1372
+ NonLoc RetVal = makeRetVal (C, E.CE ).castAs <NonLoc>();
1373
+ State = State->BindExpr (E.CE , C.getLocationContext (), RetVal);
1374
+ State = E.assumeBinOpNN (State, BO_GE, RetVal, E.getZeroVal (Call));
1375
+ if (!State)
1376
+ return ;
1377
+
1378
+ C.addTransition (State);
1379
+ }
1380
+
1381
+ void StreamChecker::preDefault (const FnDescription *Desc, const CallEvent &Call,
1382
+ CheckerContext &C) const {
1383
+ ProgramStateRef State = C.getState ();
1384
+ SVal StreamVal = getStreamArg (Desc, Call);
1385
+ State = ensureStreamNonNull (StreamVal, Call.getArgExpr (Desc->StreamArgNo ), C,
1386
+ State);
1387
+ if (!State)
1388
+ return ;
1389
+ State = ensureStreamOpened (StreamVal, C, State);
1390
+ if (!State)
1391
+ return ;
1392
+
1393
+ C.addTransition (State);
1394
+ }
1395
+
1396
+ void StreamChecker::evalSetFeofFerror (const FnDescription *Desc,
1397
+ const CallEvent &Call, CheckerContext &C,
1398
+ const StreamErrorState &ErrorKind) const {
1399
+ ProgramStateRef State = C.getState ();
1400
+ SymbolRef StreamSym = getStreamArg (Desc, Call).getAsSymbol ();
1401
+ assert (StreamSym && " Operation not permitted on non-symbolic stream value." );
1402
+ const StreamState *SS = State->get <StreamMap>(StreamSym);
1403
+ assert (SS && " Stream should be tracked by the checker." );
1404
+ State = State->set <StreamMap>(
1405
+ StreamSym, StreamState::getOpened (SS->LastOperation , ErrorKind));
1406
+ C.addTransition (State);
1407
+ }
1408
+
1380
1409
ProgramStateRef
1381
1410
StreamChecker::ensureStreamNonNull (SVal StreamVal, const Expr *StreamE,
1382
1411
CheckerContext &C,
0 commit comments