Skip to content

Commit ac3b121

Browse files
author
Your Name
committed
Handle possible values on source
1 parent f1ba660 commit ac3b121

2 files changed

Lines changed: 164 additions & 37 deletions

File tree

lib/checkstl.cpp

Lines changed: 131 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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,

test/teststl.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,6 +2727,39 @@ class TestStl : public TestFixture {
27272727
" std::copy(v0.begin(), v0.end(), v1.begin());\n"
27282728
"}\n");
27292729
ASSERT_EQUALS("", errout_str());
2730+
2731+
// all source range values are checked against the preferred destination values
2732+
check("void f(bool b) {\n"
2733+
" std::vector<int> v0;\n"
2734+
" if (b)\n"
2735+
" v0.resize(3);\n"
2736+
" else\n"
2737+
" v0.resize(10);\n"
2738+
" std::vector<int> v1(5);\n"
2739+
" std::copy(v0.begin(), v0.end(), v1.begin());\n"
2740+
"}\n");
2741+
ASSERT_EQUALS("[test.cpp:8:45]: (error) The algorithm 'std::copy' accesses 10 elements through the iterator 'v1.begin()' but only 5 elements are available. [algorithmOutOfBounds]\n",
2742+
errout_str());
2743+
2744+
check("void f(bool b) {\n"
2745+
" std::vector<int> v0;\n"
2746+
" if (b)\n"
2747+
" v0.resize(3);\n"
2748+
" else\n"
2749+
" v0.resize(5);\n"
2750+
" std::vector<int> v1(5);\n"
2751+
" std::copy(v0.begin(), v0.end(), v1.begin());\n"
2752+
"}\n");
2753+
ASSERT_EQUALS("", errout_str());
2754+
2755+
// all count values are checked as well
2756+
check("void f(bool b) {\n"
2757+
" std::vector<int> v(5);\n"
2758+
" const int n = b ? 3 : 10;\n"
2759+
" std::fill_n(v.begin(), n, 0);\n"
2760+
"}\n");
2761+
ASSERT_EQUALS("[test.cpp:4:24]: (error) The algorithm 'std::fill_n' accesses 10 elements through the iterator 'v.begin()' but only 5 elements are available. [algorithmOutOfBounds]\n",
2762+
errout_str());
27302763
}
27312764

27322765
// Dereferencing invalid pointer

0 commit comments

Comments
 (0)