@@ -3401,6 +3401,21 @@ namespace {
34013401 return !values.empty ();
34023402 }
34033403 };
3404+
3405+ // The best candidate proving an out of bounds access, preferring proofs without possible values
3406+ struct BestCandidate {
3407+ ElementCount best;
3408+ bool certain = false ;
3409+ void consider (const ElementCount& candidate) {
3410+ const bool candidateCertain = std::none_of (candidate.values .cbegin (),
3411+ candidate.values .cend (),
3412+ std::mem_fn (&ValueFlow::Value::isPossible));
3413+ if (best && (certain || !candidateCertain))
3414+ return ;
3415+ best = candidate;
3416+ certain = candidateCertain;
3417+ }
3418+ };
34043419}
34053420
34063421// Get the first ValueFlow value of a token matching the predicate, preferring known values
@@ -3486,21 +3501,15 @@ static ElementCount findInsufficientSpace(const Token* tok,
34863501 MathLib::bigint sourcePath,
34873502 const Settings& settings)
34883503{
3489- ElementCount best;
3490- bool bestIsCertain = false ;
3504+ BestCandidate insufficient;
34913505 if (!tok)
3492- return best;
3506+ return insufficient. best ;
34933507 const auto consider = [&](const ElementCount& candidate) {
34943508 if (!candidate)
34953509 return ;
34963510 if (candidate.count < 0 || accessed <= candidate.count )
34973511 return ; // the space is sufficient
3498- const bool certain =
3499- std::none_of (candidate.values .cbegin (), candidate.values .cend (), std::mem_fn (&ValueFlow::Value::isPossible));
3500- if (best && (bestIsCertain || !certain))
3501- return ;
3502- best = candidate;
3503- bestIsCertain = certain;
3512+ insufficient.consider (candidate);
35043513 };
35053514 for (const ValueFlow::Value& value : tok->values ()) {
35063515 if (!isUsableValue (value, settings) || !value.isIteratorValue ())
@@ -3520,7 +3529,87 @@ static ElementCount findInsufficientSpace(const Token* tok,
35203529 consider (getAvailableSpace (position));
35213530 }
35223531 }
3523- return best;
3532+ return insufficient.best ;
3533+ }
3534+
3535+ // Find iterator values and paired container sizes of the source range that prove more than
3536+ // <available> elements to be accessed, preferring a proof that does not rely on possible values
3537+ static ElementCount findExcessiveDistance (const Token* firstTok,
3538+ const Token* lastTok,
3539+ MathLib::bigint available,
3540+ MathLib::bigint destPath,
3541+ const Settings& settings)
3542+ {
3543+ BestCandidate excessive;
3544+ const auto consider = [&](const ElementCount& candidate) {
3545+ if (!candidate)
3546+ return ;
3547+ if (candidate.count <= 0 || candidate.count <= available)
3548+ return ; // the access is within bounds
3549+ excessive.consider (candidate);
3550+ };
3551+ for (const ValueFlow::Value& firstValue : firstTok->values ()) {
3552+ if (!isUsableValue (firstValue, settings) || !firstValue.isIteratorValue ())
3553+ continue ;
3554+ if (firstValue.path != 0 && destPath != 0 && firstValue.path != destPath)
3555+ continue ;
3556+ for (const ValueFlow::Value& lastValue : lastTok->values ()) {
3557+ if (!isUsableValue (lastValue, settings) || !lastValue.isIteratorValue ())
3558+ continue ;
3559+ IteratorPosition first, last;
3560+ first.value = &firstValue;
3561+ last.value = &lastValue;
3562+ if (first.fromEnd () == last.fromEnd ()) { // the distance does not depend on the container size
3563+ consider (getIteratorDistance (first, last));
3564+ continue ;
3565+ }
3566+ IteratorPosition& endPosition = first.fromEnd () ? first : last;
3567+ const Token* const endTok = first.fromEnd () ? firstTok : lastTok;
3568+ for (const ValueFlow::Value& sizeValue : endTok->values ()) {
3569+ if (!isUsableValue (sizeValue, settings) || !sizeValue.isContainerSizeValue () ||
3570+ sizeValue.path != endPosition.value ->path )
3571+ continue ;
3572+ endPosition.sizeValue = &sizeValue;
3573+ consider (getIteratorDistance (first, last));
3574+ }
3575+ }
3576+ }
3577+ return excessive.best ;
3578+ }
3579+
3580+ // Find count values of a count-based algorithm that prove more than <available> elements to be accessed
3581+ static ElementCount findExcessiveCount (const Token* tok,
3582+ MathLib::bigint available,
3583+ MathLib::bigint destPath,
3584+ const Settings& settings)
3585+ {
3586+ BestCandidate excessive;
3587+ if (!tok)
3588+ return excessive.best ;
3589+ for (const ValueFlow::Value& value : tok->values ()) {
3590+ if (!isUsableValue (value, settings) || !value.isIntValue ())
3591+ continue ;
3592+ // a count with an upper bound could be smaller, which would make fewer elements accessed
3593+ if (value.bound == ValueFlow::Value::Bound::Upper)
3594+ continue ;
3595+ if (value.path != 0 && destPath != 0 && value.path != destPath)
3596+ continue ;
3597+ if (value.intvalue <= available)
3598+ continue ; // the access is within bounds
3599+ ElementCount candidate;
3600+ candidate.count = value.intvalue ;
3601+ candidate.values .push_back (&value);
3602+ excessive.consider (candidate);
3603+ }
3604+ return excessive.best ;
3605+ }
3606+
3607+ // Do not warn when the proof relies on possible values on both sides
3608+ static bool bothSidesPossible (const ElementCount& accessed, const ElementCount& available)
3609+ {
3610+ const auto isPossible = std::mem_fn (&ValueFlow::Value::isPossible);
3611+ return std::any_of (accessed.values .cbegin (), accessed.values .cend (), isPossible) &&
3612+ std::any_of (available.values .cbegin (), available.values .cend (), isPossible);
35243613}
35253614
35263615// Get the number of accessed elements of a count-based algorithm such as std::fill_n
@@ -3557,27 +3646,20 @@ void CheckStlImpl::algorithmOutOfBounds()
35573646 const std::vector<const Token*> args = getArguments (nameTok);
35583647 if (args.size () < 3 )
35593648 continue ;
3560- ElementCount accessed;
3649+ ElementCount accessed; // source access count using the preferred values
35613650 std::vector<const Token*> iterArgs;
35623651 if (countBased) {
3563- const ValueFlow::Value* countValue = getCountValue (args[1 ], mSettings );
3564- if (!countValue)
3565- continue ;
3566- accessed.count = countValue->intvalue ;
3567- accessed.values .push_back (countValue);
3652+ if (const ValueFlow::Value* countValue = getCountValue (args[1 ], mSettings )) {
3653+ accessed.count = countValue->intvalue ;
3654+ accessed.values .push_back (countValue);
3655+ }
35683656 iterArgs.push_back (args[0 ]);
35693657 if (Token::simpleMatch (nameTok, " copy_n" ))
35703658 iterArgs.push_back (args[2 ]); // copy_n also writes through the third argument
35713659 } else {
35723660 // two-range overloads taking a last2 iterator do not access the second range out of bounds
35733661 if (Token::Match (nameTok, " equal|mismatch|is_permutation" ) && args.size () >= 4 && astIsIterator (args[3 ]))
35743662 continue ;
3575- const IteratorPosition first = getIteratorPosition (args[0 ], mSettings );
3576- if (!first)
3577- continue ;
3578- const IteratorPosition last = getIteratorPosition (args[1 ], mSettings );
3579- if (!last)
3580- continue ;
35813663 // both iterators must refer to the same container
35823664 const ValueFlow::Value firstLifetime = getLifetimeIteratorValue (args[0 ]);
35833665 const ValueFlow::Value lastLifetime = getLifetimeIteratorValue (args[1 ]);
@@ -3586,28 +3668,40 @@ void CheckStlImpl::algorithmOutOfBounds()
35863668 if (!isSameIteratorContainerExpression (
35873669 firstLifetime.tokvalue , lastLifetime.tokvalue , mSettings , firstLifetime.lifetimeKind ))
35883670 continue ;
3589- accessed = getIteratorDistance (first, last);
3590- if (!accessed)
3591- continue ;
3671+ const IteratorPosition first = getIteratorPosition (args[0 ], mSettings );
3672+ const IteratorPosition last = getIteratorPosition (args[1 ], mSettings );
3673+ if (first && last)
3674+ accessed = getIteratorDistance (first, last);
35923675 iterArgs.push_back (args[2 ]);
35933676 if (Token::simpleMatch (nameTok, " transform" ) && args.size () == 5 )
35943677 iterArgs.push_back (args[3 ]); // binary transform also writes through the fourth argument
35953678 }
35963679 if (accessed.count <= 0 )
3597- continue ;
3598- const MathLib::bigint sourcePath = accessed.values .front ()->path ;
3680+ accessed = ElementCount (); // there is no preferred source access count
35993681 for (const Token* const iterArg : iterArgs) {
3600- const ElementCount available = findInsufficientSpace (iterArg, accessed.count , sourcePath, mSettings );
3601- if (!available)
3602- continue ;
3603- // do not warn when the values on both sides are only possible
3604- const auto isPossible = std::mem_fn (&ValueFlow::Value::isPossible);
3605- if (std::any_of (accessed.values .cbegin (), accessed.values .cend (), isPossible) &&
3606- std::any_of (available.values .cbegin (), available.values .cend (), isPossible))
3607- continue ;
3682+ // check the preferred source access count against all destination values..
3683+ ElementCount sourceCount = accessed;
3684+ ElementCount available;
3685+ if (sourceCount)
3686+ available =
3687+ findInsufficientSpace (iterArg, sourceCount.count , sourceCount.values .front ()->path , mSettings );
3688+ if (!available || bothSidesPossible (sourceCount, available)) {
3689+ // ..or all source access counts against the preferred destination values
3690+ const IteratorPosition dest = getIteratorPosition (iterArg, mSettings );
3691+ if (!dest)
3692+ continue ;
3693+ available = getAvailableSpace (dest);
3694+ if (!available || available.count < 0 )
3695+ continue ;
3696+ sourceCount = countBased
3697+ ? findExcessiveCount (args[1 ], available.count , dest.value ->path , mSettings )
3698+ : findExcessiveDistance (args[0 ], args[1 ], available.count , dest.value ->path , mSettings );
3699+ if (!sourceCount || bothSidesPossible (sourceCount, available))
3700+ continue ;
3701+ }
36083702 const ValueFlow::Value* conditionValue = nullptr ;
36093703 bool inconclusiveValues = false ;
3610- std::vector<const ValueFlow::Value*> usedValues = accessed .values ;
3704+ std::vector<const ValueFlow::Value*> usedValues = sourceCount .values ;
36113705 usedValues.insert (usedValues.end (), available.values .cbegin (), available.values .cend ());
36123706 for (const ValueFlow::Value* value : usedValues) {
36133707 if (!conditionValue && value->condition )
@@ -3619,7 +3713,7 @@ void CheckStlImpl::algorithmOutOfBounds()
36193713 const ValueFlow::Value* pathValue = conditionValue ? conditionValue : available.values .back ();
36203714 algorithmOutOfBoundsError (iterArg,
36213715 " std::" + nameTok->str (),
3622- accessed .count ,
3716+ sourceCount .count ,
36233717 available.count ,
36243718 pathValue,
36253719 conditionValue ? conditionValue->condition : nullptr ,
0 commit comments