38
38
// Non-pure functions, for which only partial improvement over the default
39
39
// behavior is expected, are modeled via check::PostCall, non-intrusively.
40
40
//
41
- // The following standard C functions are currently supported:
42
- //
43
- // fgetc getline isdigit isupper toascii
44
- // fread isalnum isgraph isxdigit
45
- // fwrite isalpha islower read
46
- // getc isascii isprint write
47
- // getchar isblank ispunct toupper
48
- // getdelim iscntrl isspace tolower
49
- //
50
41
// ===----------------------------------------------------------------------===//
51
42
52
43
#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
@@ -384,7 +375,43 @@ class StdLibraryFunctionsChecker
384
375
};
385
376
386
377
// / The complete list of constraints that defines a single branch.
387
- typedef std::vector<ValueConstraintPtr> ConstraintSet;
378
+ using ConstraintSet = std::vector<ValueConstraintPtr>;
379
+
380
+ // / A single branch of a function summary.
381
+ // /
382
+ // / A branch is defined by a series of constraints - "assumptions" -
383
+ // / that together form a single possible outcome of invoking the function.
384
+ // / When static analyzer considers a branch, it tries to introduce
385
+ // / a child node in the Exploded Graph. The child node has to include
386
+ // / constraints that define the branch. If the constraints contradict
387
+ // / existing constraints in the state, the node is not created and the branch
388
+ // / is dropped; otherwise it's queued for future exploration.
389
+ // / The branch is accompanied by a note text that may be displayed
390
+ // / to the user when a bug is found on a path that takes this branch.
391
+ // /
392
+ // / For example, consider the branches in `isalpha(x)`:
393
+ // / Branch 1)
394
+ // / x is in range ['A', 'Z'] or in ['a', 'z']
395
+ // / then the return value is not 0. (I.e. out-of-range [0, 0])
396
+ // / and the note may say "Assuming the character is alphabetical"
397
+ // / Branch 2)
398
+ // / x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z']
399
+ // / then the return value is 0
400
+ // / and the note may say "Assuming the character is non-alphabetical".
401
+ class SummaryCase {
402
+ ConstraintSet Constraints;
403
+ StringRef Note;
404
+
405
+ public:
406
+ SummaryCase (ConstraintSet &&Constraints, StringRef Note)
407
+ : Constraints(std::move(Constraints)), Note(Note) {}
408
+
409
+ SummaryCase (const ConstraintSet &Constraints, StringRef Note)
410
+ : Constraints(Constraints), Note(Note) {}
411
+
412
+ const ConstraintSet &getConstraints () const { return Constraints; }
413
+ StringRef getNote () const { return Note; }
414
+ };
388
415
389
416
using ArgTypes = std::vector<Optional<QualType>>;
390
417
using RetType = Optional<QualType>;
@@ -451,23 +478,12 @@ class StdLibraryFunctionsChecker
451
478
return T;
452
479
}
453
480
454
- using Cases = std::vector<ConstraintSet >;
481
+ using SummaryCases = std::vector<SummaryCase >;
455
482
456
483
// / A summary includes information about
457
484
// / * function prototype (signature)
458
485
// / * approach to invalidation,
459
- // / * a list of branches - a list of list of ranges -
460
- // / A branch represents a path in the exploded graph of a function (which
461
- // / is a tree). So, a branch is a series of assumptions. In other words,
462
- // / branches represent split states and additional assumptions on top of
463
- // / the splitting assumption.
464
- // / For example, consider the branches in `isalpha(x)`
465
- // / Branch 1)
466
- // / x is in range ['A', 'Z'] or in ['a', 'z']
467
- // / then the return value is not 0. (I.e. out-of-range [0, 0])
468
- // / Branch 2)
469
- // / x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z']
470
- // / then the return value is 0.
486
+ // / * a list of branches - so, a list of list of ranges,
471
487
// / * a list of argument constraints, that must be true on every branch.
472
488
// / If these constraints are not satisfied that means a fatal error
473
489
// / usually resulting in undefined behaviour.
@@ -482,7 +498,7 @@ class StdLibraryFunctionsChecker
482
498
// / signature is matched.
483
499
class Summary {
484
500
const InvalidationKind InvalidationKd;
485
- Cases CaseConstraints ;
501
+ SummaryCases Cases ;
486
502
ConstraintSet ArgConstraints;
487
503
488
504
// The function to which the summary applies. This is set after lookup and
@@ -492,12 +508,12 @@ class StdLibraryFunctionsChecker
492
508
public:
493
509
Summary (InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {}
494
510
495
- Summary &Case (ConstraintSet &&CS) {
496
- CaseConstraints .push_back (std::move (CS));
511
+ Summary &Case (ConstraintSet &&CS, StringRef Note = " " ) {
512
+ Cases .push_back (SummaryCase ( std::move (CS), Note ));
497
513
return *this ;
498
514
}
499
- Summary &Case (const ConstraintSet &CS) {
500
- CaseConstraints .push_back (CS );
515
+ Summary &Case (const ConstraintSet &CS, StringRef Note = " " ) {
516
+ Cases .push_back (SummaryCase (CS, Note) );
501
517
return *this ;
502
518
}
503
519
Summary &ArgConstraint (ValueConstraintPtr VC) {
@@ -508,7 +524,7 @@ class StdLibraryFunctionsChecker
508
524
}
509
525
510
526
InvalidationKind getInvalidationKd () const { return InvalidationKd; }
511
- const Cases & getCaseConstraints () const { return CaseConstraints ; }
527
+ const SummaryCases & getCases () const { return Cases ; }
512
528
const ConstraintSet &getArgConstraints () const { return ArgConstraints; }
513
529
514
530
QualType getArgType (ArgNo ArgN) const {
@@ -530,8 +546,8 @@ class StdLibraryFunctionsChecker
530
546
// Once we know the exact type of the function then do validation check on
531
547
// all the given constraints.
532
548
bool validateByConstraints (const FunctionDecl *FD) const {
533
- for (const ConstraintSet &Case : CaseConstraints )
534
- for (const ValueConstraintPtr &Constraint : Case)
549
+ for (const SummaryCase &Case : Cases )
550
+ for (const ValueConstraintPtr &Constraint : Case. getConstraints () )
535
551
if (!Constraint->checkValidity (FD))
536
552
return false ;
537
553
for (const ValueConstraintPtr &Constraint : ArgConstraints)
@@ -842,18 +858,29 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
842
858
// Now apply the constraints.
843
859
const Summary &Summary = *FoundSummary;
844
860
ProgramStateRef State = C.getState ();
861
+ const ExplodedNode *Node = C.getPredecessor ();
845
862
846
863
// Apply case/branch specifications.
847
- for (const ConstraintSet &Case : Summary.getCaseConstraints ()) {
864
+ for (const SummaryCase &Case : Summary.getCases ()) {
848
865
ProgramStateRef NewState = State;
849
- for (const ValueConstraintPtr &Constraint : Case) {
866
+ for (const ValueConstraintPtr &Constraint : Case. getConstraints () ) {
850
867
NewState = Constraint->apply (NewState, Call, Summary, C);
851
868
if (!NewState)
852
869
break ;
853
870
}
854
871
855
- if (NewState && NewState != State)
856
- C.addTransition (NewState);
872
+ if (NewState && NewState != State) {
873
+ StringRef Note = Case.getNote ();
874
+ const NoteTag *Tag = C.getNoteTag (
875
+ // Sorry couldn't help myself.
876
+ [Node, Note]() {
877
+ // Don't emit "Assuming..." note when we ended up
878
+ // knowing in advance which branch is taken.
879
+ return (Node->succ_size () > 1 ) ? Note.str () : " " ;
880
+ },
881
+ /* IsPrunable=*/ true );
882
+ C.addTransition (NewState, Tag);
883
+ }
857
884
}
858
885
}
859
886
@@ -1211,7 +1238,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
1211
1238
// Boils down to isupper() or islower() or isdigit().
1212
1239
.Case ({ArgumentCondition (0U , WithinRange,
1213
1240
{{' 0' , ' 9' }, {' A' , ' Z' }, {' a' , ' z' }}),
1214
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1241
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1242
+ " Assuming the character is alphanumeric" )
1215
1243
// The locale-specific range.
1216
1244
// No post-condition. We are completely unaware of
1217
1245
// locale-specific return values.
@@ -1220,65 +1248,81 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
1220
1248
{ArgumentCondition (
1221
1249
0U , OutOfRange,
1222
1250
{{' 0' , ' 9' }, {' A' , ' Z' }, {' a' , ' z' }, {128 , UCharRangeMax}}),
1223
- ReturnValueCondition (WithinRange, SingleValue (0 ))})
1251
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1252
+ " Assuming the character is non-alphanumeric" )
1224
1253
.ArgConstraint (ArgumentCondition (
1225
1254
0U , WithinRange, {{EOFv, EOFv}, {0 , UCharRangeMax}})));
1226
1255
addToFunctionSummaryMap (
1227
1256
" isalpha" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1228
1257
Summary (EvalCallAsPure)
1229
1258
.Case ({ArgumentCondition (0U , WithinRange, {{' A' , ' Z' }, {' a' , ' z' }}),
1230
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1259
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1260
+ " Assuming the character is alphabetical" )
1231
1261
// The locale-specific range.
1232
1262
.Case ({ArgumentCondition (0U , WithinRange, {{128 , UCharRangeMax}})})
1233
1263
.Case ({ArgumentCondition (
1234
1264
0U , OutOfRange,
1235
1265
{{' A' , ' Z' }, {' a' , ' z' }, {128 , UCharRangeMax}}),
1236
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1266
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1267
+ " Assuming the character is non-alphabetical" ));
1237
1268
addToFunctionSummaryMap (
1238
1269
" isascii" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1239
1270
Summary (EvalCallAsPure)
1240
1271
.Case ({ArgumentCondition (0U , WithinRange, Range (0 , 127 )),
1241
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1272
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1273
+ " Assuming the character is an ASCII character" )
1242
1274
.Case ({ArgumentCondition (0U , OutOfRange, Range (0 , 127 )),
1243
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1275
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1276
+ " Assuming the character is not an ASCII character" ));
1244
1277
addToFunctionSummaryMap (
1245
1278
" isblank" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1246
1279
Summary (EvalCallAsPure)
1247
1280
.Case ({ArgumentCondition (0U , WithinRange, {{' \t ' , ' \t ' }, {' ' , ' ' }}),
1248
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1281
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1282
+ " Assuming the character is a blank character" )
1249
1283
.Case ({ArgumentCondition (0U , OutOfRange, {{' \t ' , ' \t ' }, {' ' , ' ' }}),
1250
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1284
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1285
+ " Assuming the character is not a blank character" ));
1251
1286
addToFunctionSummaryMap (
1252
1287
" iscntrl" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1253
1288
Summary (EvalCallAsPure)
1254
1289
.Case ({ArgumentCondition (0U , WithinRange, {{0 , 32 }, {127 , 127 }}),
1255
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1290
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1291
+ " Assuming the character is a control character" )
1256
1292
.Case ({ArgumentCondition (0U , OutOfRange, {{0 , 32 }, {127 , 127 }}),
1257
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1293
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1294
+ " Assuming the character is not a control character" ));
1258
1295
addToFunctionSummaryMap (
1259
1296
" isdigit" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1260
1297
Summary (EvalCallAsPure)
1261
1298
.Case ({ArgumentCondition (0U , WithinRange, Range (' 0' , ' 9' )),
1262
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1299
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1300
+ " Assuming the character is a digit" )
1263
1301
.Case ({ArgumentCondition (0U , OutOfRange, Range (' 0' , ' 9' )),
1264
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1302
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1303
+ " Assuming the character is not a digit" ));
1265
1304
addToFunctionSummaryMap (
1266
1305
" isgraph" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1267
1306
Summary (EvalCallAsPure)
1268
1307
.Case ({ArgumentCondition (0U , WithinRange, Range (33 , 126 )),
1269
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1270
- .Case ({ArgumentCondition (0U , OutOfRange, Range (33 , 126 )),
1271
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1308
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1309
+ " Assuming the character has graphical representation" )
1310
+ .Case (
1311
+ {ArgumentCondition (0U , OutOfRange, Range (33 , 126 )),
1312
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1313
+ " Assuming the character does not have graphical representation" ));
1272
1314
addToFunctionSummaryMap (
1273
1315
" islower" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1274
1316
Summary (EvalCallAsPure)
1275
1317
// Is certainly lowercase.
1276
1318
.Case ({ArgumentCondition (0U , WithinRange, Range (' a' , ' z' )),
1277
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1319
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1320
+ " Assuming the character is a lowercase letter" )
1278
1321
// Is ascii but not lowercase.
1279
1322
.Case ({ArgumentCondition (0U , WithinRange, Range (0 , 127 )),
1280
1323
ArgumentCondition (0U , OutOfRange, Range (' a' , ' z' )),
1281
- ReturnValueCondition (WithinRange, SingleValue (0 ))})
1324
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1325
+ " Assuming the character is not a lowercase letter" )
1282
1326
// The locale-specific range.
1283
1327
.Case ({ArgumentCondition (0U , WithinRange, {{128 , UCharRangeMax}})})
1284
1328
// Is not an unsigned char.
@@ -1288,52 +1332,62 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
1288
1332
" isprint" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1289
1333
Summary (EvalCallAsPure)
1290
1334
.Case ({ArgumentCondition (0U , WithinRange, Range (32 , 126 )),
1291
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1335
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1336
+ " Assuming the character is printable" )
1292
1337
.Case ({ArgumentCondition (0U , OutOfRange, Range (32 , 126 )),
1293
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1338
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1339
+ " Assuming the character is non-printable" ));
1294
1340
addToFunctionSummaryMap (
1295
1341
" ispunct" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1296
1342
Summary (EvalCallAsPure)
1297
1343
.Case ({ArgumentCondition (
1298
1344
0U , WithinRange,
1299
1345
{{' !' , ' /' }, {' :' , ' @' }, {' [' , ' `' }, {' {' , ' ~' }}),
1300
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1346
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1347
+ " Assuming the character is a punctuation mark" )
1301
1348
.Case ({ArgumentCondition (
1302
1349
0U , OutOfRange,
1303
1350
{{' !' , ' /' }, {' :' , ' @' }, {' [' , ' `' }, {' {' , ' ~' }}),
1304
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1351
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1352
+ " Assuming the character is not a punctuation mark" ));
1305
1353
addToFunctionSummaryMap (
1306
1354
" isspace" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1307
1355
Summary (EvalCallAsPure)
1308
1356
// Space, '\f', '\n', '\r', '\t', '\v'.
1309
1357
.Case ({ArgumentCondition (0U , WithinRange, {{9 , 13 }, {' ' , ' ' }}),
1310
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1358
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1359
+ " Assuming the character is a whitespace character" )
1311
1360
// The locale-specific range.
1312
1361
.Case ({ArgumentCondition (0U , WithinRange, {{128 , UCharRangeMax}})})
1313
1362
.Case ({ArgumentCondition (0U , OutOfRange,
1314
1363
{{9 , 13 }, {' ' , ' ' }, {128 , UCharRangeMax}}),
1315
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1364
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1365
+ " Assuming the character is not a whitespace character" ));
1316
1366
addToFunctionSummaryMap (
1317
1367
" isupper" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1318
1368
Summary (EvalCallAsPure)
1319
1369
// Is certainly uppercase.
1320
1370
.Case ({ArgumentCondition (0U , WithinRange, Range (' A' , ' Z' )),
1321
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1371
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1372
+ " Assuming the character is an uppercase letter" )
1322
1373
// The locale-specific range.
1323
1374
.Case ({ArgumentCondition (0U , WithinRange, {{128 , UCharRangeMax}})})
1324
1375
// Other.
1325
1376
.Case ({ArgumentCondition (0U , OutOfRange,
1326
1377
{{' A' , ' Z' }, {128 , UCharRangeMax}}),
1327
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1378
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1379
+ " Assuming the character is not an uppercase letter" ));
1328
1380
addToFunctionSummaryMap (
1329
1381
" isxdigit" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1330
1382
Summary (EvalCallAsPure)
1331
1383
.Case ({ArgumentCondition (0U , WithinRange,
1332
1384
{{' 0' , ' 9' }, {' A' , ' F' }, {' a' , ' f' }}),
1333
- ReturnValueCondition (OutOfRange, SingleValue (0 ))})
1385
+ ReturnValueCondition (OutOfRange, SingleValue (0 ))},
1386
+ " Assuming the character is a hexadecimal digit" )
1334
1387
.Case ({ArgumentCondition (0U , OutOfRange,
1335
1388
{{' 0' , ' 9' }, {' A' , ' F' }, {' a' , ' f' }}),
1336
- ReturnValueCondition (WithinRange, SingleValue (0 ))}));
1389
+ ReturnValueCondition (WithinRange, SingleValue (0 ))},
1390
+ " Assuming the character is not a hexadecimal digit" ));
1337
1391
addToFunctionSummaryMap (
1338
1392
" toupper" , Signature (ArgTypes{IntTy}, RetType{IntTy}),
1339
1393
Summary (EvalCallAsPure)
@@ -1435,12 +1489,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
1435
1489
GetLineSummary);
1436
1490
1437
1491
{
1438
- Summary GetenvSummary = Summary (NoEvalCall)
1439
- .ArgConstraint (NotNull (ArgNo (0 )))
1440
- .Case ({NotNull (Ret)});
1492
+ Summary GetenvSummary =
1493
+ Summary (NoEvalCall)
1494
+ .ArgConstraint (NotNull (ArgNo (0 )))
1495
+ .Case ({NotNull (Ret)}, " Assuming the environment variable exists" );
1441
1496
// In untrusted environments the envvar might not exist.
1442
1497
if (!ShouldAssumeControlledEnvironment)
1443
- GetenvSummary.Case ({NotNull (Ret)->negate ()});
1498
+ GetenvSummary.Case ({NotNull (Ret)->negate ()},
1499
+ " Assuming the environment variable does not exist" );
1444
1500
1445
1501
// char *getenv(const char *name);
1446
1502
addToFunctionSummaryMap (
0 commit comments