12
12
// ===----------------------------------------------------------------------===//
13
13
14
14
#include " InterCheckerAPI.h"
15
+ #include " clang/AST/OperationKinds.h"
15
16
#include " clang/Basic/Builtins.h"
16
17
#include " clang/Basic/CharInfo.h"
17
18
#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
22
23
#include " clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23
24
#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24
25
#include " clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
26
+ #include " clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
25
27
#include " clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
28
+ #include " clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
29
+ #include " llvm/ADT/APSInt.h"
26
30
#include " llvm/ADT/STLExtras.h"
27
- #include " llvm/ADT/SmallString.h"
28
31
#include " llvm/ADT/StringExtras.h"
32
+ #include " llvm/Support/Casting.h"
29
33
#include " llvm/Support/raw_ostream.h"
30
34
#include < functional>
31
35
#include < optional>
@@ -304,6 +308,10 @@ class CStringChecker : public Checker< eval::Call,
304
308
// Re-usable checks
305
309
ProgramStateRef checkNonNull (CheckerContext &C, ProgramStateRef State,
306
310
AnyArgExpr Arg, SVal l) const ;
311
+ // Check whether the origin region behind \p Element (like the actual array
312
+ // region \p Element is from) is initialized.
313
+ ProgramStateRef checkInit (CheckerContext &C, ProgramStateRef state,
314
+ AnyArgExpr Buffer, SVal Element, SVal Size) const ;
307
315
ProgramStateRef CheckLocation (CheckerContext &C, ProgramStateRef state,
308
316
AnyArgExpr Buffer, SVal Element,
309
317
AccessKind Access,
@@ -329,7 +337,7 @@ class CStringChecker : public Checker< eval::Call,
329
337
const Stmt *S, StringRef WarningMsg) const ;
330
338
void emitAdditionOverflowBug (CheckerContext &C, ProgramStateRef State) const ;
331
339
void emitUninitializedReadBug (CheckerContext &C, ProgramStateRef State,
332
- const Expr *E) const ;
340
+ const Expr *E, StringRef Msg ) const ;
333
341
ProgramStateRef checkAdditionOverflow (CheckerContext &C,
334
342
ProgramStateRef state,
335
343
NonLoc left,
@@ -351,16 +359,16 @@ REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal)
351
359
// Individual checks and utility methods.
352
360
// ===----------------------------------------------------------------------===//
353
361
354
- std::pair<ProgramStateRef , ProgramStateRef >
355
- CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state , SVal V,
362
+ std::pair<ProgramStateRef, ProgramStateRef>
363
+ CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef State , SVal V,
356
364
QualType Ty) {
357
365
std::optional<DefinedSVal> val = V.getAs <DefinedSVal>();
358
366
if (!val)
359
- return std::pair<ProgramStateRef , ProgramStateRef >(state, state );
367
+ return std::pair<ProgramStateRef, ProgramStateRef>(State, State );
360
368
361
369
SValBuilder &svalBuilder = C.getSValBuilder ();
362
370
DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal (Ty);
363
- return state ->assume (svalBuilder.evalEQ (state , *val, zero));
371
+ return State ->assume (svalBuilder.evalEQ (State , *val, zero));
364
372
}
365
373
366
374
ProgramStateRef CStringChecker::checkNonNull (CheckerContext &C,
@@ -393,6 +401,149 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
393
401
return stateNonNull;
394
402
}
395
403
404
+ static std::optional<NonLoc> getIndex (ProgramStateRef State,
405
+ const ElementRegion *ER, CharKind CK) {
406
+ SValBuilder &SVB = State->getStateManager ().getSValBuilder ();
407
+ ASTContext &Ctx = SVB.getContext ();
408
+
409
+ if (CK == CharKind::Regular) {
410
+ if (ER->getValueType () != Ctx.CharTy )
411
+ return {};
412
+ return ER->getIndex ();
413
+ }
414
+
415
+ if (ER->getValueType () != Ctx.WideCharTy )
416
+ return {};
417
+
418
+ QualType SizeTy = Ctx.getSizeType ();
419
+ NonLoc WideSize =
420
+ SVB.makeIntVal (Ctx.getTypeSizeInChars (Ctx.WideCharTy ).getQuantity (),
421
+ SizeTy)
422
+ .castAs <NonLoc>();
423
+ SVal Offset =
424
+ SVB.evalBinOpNN (State, BO_Mul, ER->getIndex (), WideSize, SizeTy);
425
+ if (Offset.isUnknown ())
426
+ return {};
427
+ return Offset.castAs <NonLoc>();
428
+ }
429
+
430
+ // Basically 1 -> 1st, 12 -> 12th, etc.
431
+ static void printIdxWithOrdinalSuffix (llvm::raw_ostream &Os, unsigned Idx) {
432
+ Os << Idx << llvm::getOrdinalSuffix (Idx);
433
+ }
434
+
435
+ ProgramStateRef CStringChecker::checkInit (CheckerContext &C,
436
+ ProgramStateRef State,
437
+ AnyArgExpr Buffer, SVal Element,
438
+ SVal Size) const {
439
+
440
+ // If a previous check has failed, propagate the failure.
441
+ if (!State)
442
+ return nullptr ;
443
+
444
+ const MemRegion *R = Element.getAsRegion ();
445
+ const auto *ER = dyn_cast_or_null<ElementRegion>(R);
446
+ if (!ER)
447
+ return State;
448
+
449
+ const auto *SuperR = ER->getSuperRegion ()->getAs <TypedValueRegion>();
450
+ if (!SuperR)
451
+ return State;
452
+
453
+ // FIXME: We ought to able to check objects as well. Maybe
454
+ // UninitializedObjectChecker could help?
455
+ if (!SuperR->getValueType ()->isArrayType ())
456
+ return State;
457
+
458
+ SValBuilder &SVB = C.getSValBuilder ();
459
+ ASTContext &Ctx = SVB.getContext ();
460
+
461
+ const QualType ElemTy = Ctx.getBaseElementType (SuperR->getValueType ());
462
+ const NonLoc Zero = SVB.makeZeroArrayIndex ();
463
+
464
+ std::optional<Loc> FirstElementVal =
465
+ State->getLValue (ElemTy, Zero, loc::MemRegionVal (SuperR)).getAs <Loc>();
466
+ if (!FirstElementVal)
467
+ return State;
468
+
469
+ // Ensure that we wouldn't read uninitialized value.
470
+ if (Filter.CheckCStringUninitializedRead &&
471
+ State->getSVal (*FirstElementVal).isUndef ()) {
472
+ llvm::SmallString<258 > Buf;
473
+ llvm::raw_svector_ostream OS (Buf);
474
+ OS << " The first element of the " ;
475
+ printIdxWithOrdinalSuffix (OS, Buffer.ArgumentIndex + 1 );
476
+ OS << " argument is undefined" ;
477
+ emitUninitializedReadBug (C, State, Buffer.Expression , OS.str ());
478
+ return nullptr ;
479
+ }
480
+
481
+ // We won't check whether the entire region is fully initialized -- lets just
482
+ // check that the first and the last element is. So, onto checking the last
483
+ // element:
484
+ const QualType IdxTy = SVB.getArrayIndexType ();
485
+
486
+ NonLoc ElemSize =
487
+ SVB.makeIntVal (Ctx.getTypeSizeInChars (ElemTy).getQuantity (), IdxTy)
488
+ .castAs <NonLoc>();
489
+
490
+ // FIXME: Check that the size arg to the cstring function is divisible by
491
+ // size of the actual element type?
492
+
493
+ // The type of the argument to the cstring function is either char or wchar,
494
+ // but thats not the type of the original array (or memory region).
495
+ // Suppose the following:
496
+ // int t[5];
497
+ // memcpy(dst, t, sizeof(t) / sizeof(t[0]));
498
+ // When checking whether t is fully initialized, we see it as char array of
499
+ // size sizeof(int)*5. If we check the last element as a character, we read
500
+ // the last byte of an integer, which will be undefined. But just because
501
+ // that value is undefined, it doesn't mean that the element is uninitialized!
502
+ // For this reason, we need to retrieve the actual last element with the
503
+ // correct type.
504
+
505
+ // Divide the size argument to the cstring function by the actual element
506
+ // type. This value will be size of the array, or the index to the
507
+ // past-the-end element.
508
+ std::optional<NonLoc> Offset =
509
+ SVB.evalBinOpNN (State, clang::BO_Div, Size.castAs <NonLoc>(), ElemSize,
510
+ IdxTy)
511
+ .getAs <NonLoc>();
512
+
513
+ // Retrieve the index of the last element.
514
+ const NonLoc One = SVB.makeIntVal (1 , IdxTy).castAs <NonLoc>();
515
+ SVal LastIdx = SVB.evalBinOpNN (State, BO_Sub, *Offset, One, IdxTy);
516
+
517
+ if (!Offset)
518
+ return State;
519
+
520
+ SVal LastElementVal =
521
+ State->getLValue (ElemTy, LastIdx, loc::MemRegionVal (SuperR));
522
+ if (!isa<Loc>(LastElementVal))
523
+ return State;
524
+
525
+ if (Filter.CheckCStringUninitializedRead &&
526
+ State->getSVal (LastElementVal.castAs <Loc>()).isUndef ()) {
527
+ const llvm::APSInt *IdxInt = LastIdx.getAsInteger ();
528
+ // If we can't get emit a sensible last element index, just bail out --
529
+ // prefer to emit nothing in favour of emitting garbage quality reports.
530
+ if (!IdxInt) {
531
+ C.addSink ();
532
+ return nullptr ;
533
+ }
534
+ llvm::SmallString<258 > Buf;
535
+ llvm::raw_svector_ostream OS (Buf);
536
+ OS << " The last accessed element (at index " ;
537
+ OS << IdxInt->getExtValue ();
538
+ OS << " ) in the " ;
539
+ printIdxWithOrdinalSuffix (OS, Buffer.ArgumentIndex + 1 );
540
+ OS << " argument is undefined" ;
541
+ emitUninitializedReadBug (C, State, Buffer.Expression , OS.str ());
542
+ return nullptr ;
543
+ }
544
+ return State;
545
+ }
546
+
396
547
// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor?
397
548
ProgramStateRef CStringChecker::CheckLocation (CheckerContext &C,
398
549
ProgramStateRef state,
@@ -413,38 +564,17 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
413
564
if (!ER)
414
565
return state;
415
566
416
- SValBuilder &svalBuilder = C.getSValBuilder ();
417
- ASTContext &Ctx = svalBuilder.getContext ();
418
-
419
567
// Get the index of the accessed element.
420
- NonLoc Idx = ER->getIndex ();
421
-
422
- if (CK == CharKind::Regular) {
423
- if (ER->getValueType () != Ctx.CharTy )
424
- return state;
425
- } else {
426
- if (ER->getValueType () != Ctx.WideCharTy )
427
- return state;
428
-
429
- QualType SizeTy = Ctx.getSizeType ();
430
- NonLoc WideSize =
431
- svalBuilder
432
- .makeIntVal (Ctx.getTypeSizeInChars (Ctx.WideCharTy ).getQuantity (),
433
- SizeTy)
434
- .castAs <NonLoc>();
435
- SVal Offset = svalBuilder.evalBinOpNN (state, BO_Mul, Idx, WideSize, SizeTy);
436
- if (Offset.isUnknown ())
437
- return state;
438
- Idx = Offset.castAs <NonLoc>();
439
- }
568
+ std::optional<NonLoc> Idx = getIndex (state, ER, CK);
569
+ if (!Idx)
570
+ return state;
440
571
441
572
// Get the size of the array.
442
573
const auto *superReg = cast<SubRegion>(ER->getSuperRegion ());
443
574
DefinedOrUnknownSVal Size =
444
575
getDynamicExtent (state, superReg, C.getSValBuilder ());
445
576
446
- ProgramStateRef StInBound, StOutBound;
447
- std::tie (StInBound, StOutBound) = state->assumeInBoundDual (Idx, Size);
577
+ auto [StInBound, StOutBound] = state->assumeInBoundDual (*Idx, Size);
448
578
if (StOutBound && !StInBound) {
449
579
// These checks are either enabled by the CString out-of-bounds checker
450
580
// explicitly or implicitly by the Malloc checker.
@@ -459,15 +589,6 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
459
589
return nullptr ;
460
590
}
461
591
462
- // Ensure that we wouldn't read uninitialized value.
463
- if (Access == AccessKind::read) {
464
- if (Filter.CheckCStringUninitializedRead &&
465
- StInBound->getSVal (ER).isUndef ()) {
466
- emitUninitializedReadBug (C, StInBound, Buffer.Expression );
467
- return nullptr ;
468
- }
469
- }
470
-
471
592
// Array bound check succeeded. From this point forward the array bound
472
593
// should always succeed.
473
594
return StInBound;
@@ -502,6 +623,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
502
623
503
624
// Check if the first byte of the buffer is accessible.
504
625
State = CheckLocation (C, State, Buffer, BufStart, Access, CK);
626
+
505
627
if (!State)
506
628
return nullptr ;
507
629
@@ -526,6 +648,8 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
526
648
SVal BufEnd =
527
649
svalBuilder.evalBinOpLN (State, BO_Add, *BufLoc, LastOffset, PtrTy);
528
650
State = CheckLocation (C, State, Buffer, BufEnd, Access, CK);
651
+ if (Access == AccessKind::read)
652
+ State = checkInit (C, State, Buffer, BufEnd, *Length);
529
653
530
654
// If the buffer isn't large enough, abort.
531
655
if (!State)
@@ -694,16 +818,17 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
694
818
695
819
void CStringChecker::emitUninitializedReadBug (CheckerContext &C,
696
820
ProgramStateRef State,
697
- const Expr *E) const {
821
+ const Expr *E,
822
+ StringRef Msg) const {
698
823
if (ExplodedNode *N = C.generateErrorNode (State)) {
699
- const char *Msg =
700
- " Bytes string function accesses uninitialized/garbage values" ;
701
824
if (!BT_UninitRead)
702
825
BT_UninitRead.reset (new BugType (Filter.CheckNameCStringUninitializedRead ,
703
826
" Accessing unitialized/garbage values" ));
704
827
705
828
auto Report =
706
829
std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N);
830
+ Report->addNote (" Other elements might also be undefined" ,
831
+ Report->getLocation ());
707
832
Report->addRange (E->getSourceRange ());
708
833
bugreporter::trackExpressionValue (N, E, *Report);
709
834
C.emitReport (std::move (Report));
0 commit comments