Skip to content

Commit dc385f3

Browse files
Fix #12775 FP containerOutOfBounds with push_back() in loop (#6451)
1 parent 43e6e6e commit dc385f3

File tree

10 files changed

+67
-21
lines changed

10 files changed

+67
-21
lines changed

Diff for: .github/workflows/cppcheck-premium.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ jobs:
2222

2323
build:
2424
runs-on: ubuntu-22.04 # run on the latest image only
25-
25+
# FIXME: enable after update
26+
if: false
2627
steps:
2728
- uses: actions/checkout@v3
2829

Diff for: cfg/cppcheck-cfg.rng

+5
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,11 @@
252252
<ref name="ARG-DIRECTION"/>
253253
</attribute>
254254
</optional>
255+
<optional>
256+
<attribute name="indirect">
257+
<ref name="INDIRECT"/>
258+
</attribute>
259+
</optional>
255260

256261
<interleave>
257262
<optional>

Diff for: cfg/std.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -6729,7 +6729,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
67296729
<function name="std::deque::push_back,std::deque::push_front,std::list::push_back,std::list::push_front,std::forward_list::push_front,std::queue::push,std::priority_queue::push,std::stack::push,std::vector::push_back">
67306730
<noreturn>false</noreturn>
67316731
<returnValue type="void"/>
6732-
<arg nr="1">
6732+
<arg nr="1" direction="in" indirect="0">
67336733
<not-uninit/>
67346734
</arg>
67356735
</function>

Diff for: lib/astutils.cpp

+11-4
Original file line numberDiff line numberDiff line change
@@ -2500,7 +2500,7 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti
25002500

25012501
if (!tok->function() && !tok->variable() && tok->isName()) {
25022502
// Check if direction (in, out, inout) is specified in the library configuration and use that
2503-
const Library::ArgumentChecks::Direction argDirection = settings.library.getArgDirection(tok, 1 + argnr);
2503+
const Library::ArgumentChecks::Direction argDirection = settings.library.getArgDirection(tok, 1 + argnr, indirect);
25042504
if (argDirection == Library::ArgumentChecks::Direction::DIR_IN)
25052505
return false;
25062506

@@ -2843,9 +2843,16 @@ static bool isExpressionChangedAt(const F& getExprTok,
28432843
if (!isMutableExpression(tok))
28442844
return false;
28452845
if (tok->exprId() != exprid || (!tok->varId() && !tok->isName())) {
2846-
if (globalvar && Token::Match(tok, "%name% (") && !(tok->function() && tok->function()->isAttributePure()))
2847-
// TODO: Is global variable really changed by function call?
2848-
return true;
2846+
if (globalvar && Token::Match(tok, "%name% (") &&
2847+
(!(tok->function() && (tok->function()->isAttributePure() || tok->function()->isAttributeConst())))) {
2848+
if (!Token::simpleMatch(tok->astParent(), "."))
2849+
return true;
2850+
const auto yield = astContainerYield(tok->astParent()->astOperand1());
2851+
if (yield != Library::Container::Yield::SIZE && yield != Library::Container::Yield::EMPTY &&
2852+
yield != Library::Container::Yield::BUFFER && yield != Library::Container::Yield::BUFFER_NT)
2853+
// TODO: Is global variable really changed by function call?
2854+
return true;
2855+
}
28492856
int i = 1;
28502857
bool aliased = false;
28512858
// If we can't find the expression then assume it is an alias

Diff for: lib/checkother.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1638,6 +1638,9 @@ void CheckOther::checkConstPointer()
16381638
if (p->isMaybeUnused())
16391639
continue;
16401640
}
1641+
if (const Function* func = Scope::nestedInFunction(p->scope()))
1642+
if (func->templateDef)
1643+
continue;
16411644
if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), p) == nonConstPointers.cend()) {
16421645
// const Token *start = getVariableChangedStart(p);
16431646
// const int indirect = p->isArray() ? p->dimensions().size() : 1;
@@ -2912,7 +2915,7 @@ void CheckOther::checkRedundantCopy()
29122915
if (Token::simpleMatch(dot, ".")) {
29132916
const Token* varTok = dot->astOperand1();
29142917
const int indirect = varTok->valueType() ? varTok->valueType()->pointer : 0;
2915-
if (isVariableChanged(tok, tok->scope()->bodyEnd, varTok->varId(), indirect, /*globalvar*/ false, *mSettings))
2918+
if (isVariableChanged(tok, tok->scope()->bodyEnd, indirect, varTok->varId(), /*globalvar*/ true, *mSettings))
29162919
continue;
29172920
if (isTemporary(dot, &mSettings->library, /*unknown*/ true))
29182921
continue;

Diff for: lib/fwdanalysis.cpp

+2-6
Original file line numberDiff line numberDiff line change
@@ -361,12 +361,8 @@ FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *
361361
while (argnr < args.size() && args[argnr] != parent)
362362
argnr++;
363363
if (argnr < args.size()) {
364-
const Library::Function* functionInfo = mSettings.library.getFunction(ftok->astOperand1());
365-
if (functionInfo) {
366-
const auto it = functionInfo->argumentChecks.find(argnr + 1);
367-
if (it != functionInfo->argumentChecks.end() && it->second.direction == Library::ArgumentChecks::Direction::DIR_OUT)
368-
continue;
369-
}
364+
if (mSettings.library.getArgDirection(ftok->astOperand1(), argnr + 1) == Library::ArgumentChecks::Direction::DIR_OUT)
365+
continue;
370366
}
371367
}
372368
return Result(Result::Type::BAILOUT, parent->astParent());

Diff for: lib/library.cpp

+16-6
Original file line numberDiff line numberDiff line change
@@ -738,13 +738,20 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co
738738
const char * const argDirection = functionnode->Attribute("direction");
739739
if (argDirection) {
740740
const size_t argDirLen = strlen(argDirection);
741+
ArgumentChecks::Direction dir = ArgumentChecks::Direction::DIR_UNKNOWN;
741742
if (!strncmp(argDirection, "in", argDirLen)) {
742-
ac.direction = ArgumentChecks::Direction::DIR_IN;
743+
dir = ArgumentChecks::Direction::DIR_IN;
743744
} else if (!strncmp(argDirection, "out", argDirLen)) {
744-
ac.direction = ArgumentChecks::Direction::DIR_OUT;
745+
dir = ArgumentChecks::Direction::DIR_OUT;
745746
} else if (!strncmp(argDirection, "inout", argDirLen)) {
746-
ac.direction = ArgumentChecks::Direction::DIR_INOUT;
747+
dir = ArgumentChecks::Direction::DIR_INOUT;
747748
}
749+
if (const char* const argIndirect = functionnode->Attribute("indirect")) {
750+
const int indirect = strToInt<int>(argIndirect);
751+
ac.direction[indirect] = dir; // TODO: handle multiple directions/indirect levels
752+
}
753+
else
754+
ac.direction.fill(dir);
748755
}
749756
for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) {
750757
const std::string argnodename = argnode->Name();
@@ -1500,11 +1507,14 @@ bool Library::hasminsize(const Token *ftok) const
15001507
});
15011508
}
15021509

1503-
Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr) const
1510+
Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr, int indirect) const
15041511
{
15051512
const ArgumentChecks* arg = getarg(ftok, argnr);
1506-
if (arg)
1507-
return arg->direction;
1513+
if (arg) {
1514+
if (indirect < 0 || indirect >= arg->direction.size())
1515+
throw InternalError(ftok, "Bad indirect value: " + std::to_string(indirect));
1516+
return arg->direction[indirect];
1517+
}
15081518
if (formatstr_function(ftok)) {
15091519
const int fs_argno = formatstr_argno(ftok);
15101520
if (fs_argno >= 0 && argnr >= fs_argno) {

Diff for: lib/library.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ class CPPCHECKLIB Library {
291291
DIR_INOUT, ///< Input to called function, and output to caller. Data is passed by reference or address and is potentially modified.
292292
DIR_UNKNOWN ///< direction not known / specified
293293
};
294-
Direction direction = Direction::DIR_UNKNOWN;
294+
// argument directions up to ** indirect level (only one can be configured explicitly at the moment)
295+
std::array<Direction, 3> direction = { { Direction::DIR_UNKNOWN, Direction::DIR_UNKNOWN, Direction::DIR_UNKNOWN } };
295296
};
296297

297298
struct Function {
@@ -355,7 +356,7 @@ class CPPCHECKLIB Library {
355356
return arg ? &arg->minsizes : nullptr;
356357
}
357358

358-
ArgumentChecks::Direction getArgDirection(const Token* ftok, int argnr) const;
359+
ArgumentChecks::Direction getArgDirection(const Token* ftok, int argnr, int indirect = 0) const;
359360

360361
bool markupFile(const std::string &path) const;
361362

Diff for: lib/valueflow.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1747,6 +1747,12 @@ struct ValueFlowAnalyzer : Analyzer {
17471747
indirect = vt->pointer;
17481748
if (vt->type == ValueType::ITERATOR)
17491749
++indirect;
1750+
const Token* tok2 = tok;
1751+
while (Token::simpleMatch(tok2->astParent(), "[")) {
1752+
tok2 = tok2->astParent();
1753+
--indirect;
1754+
}
1755+
indirect = std::max(indirect, 0);
17501756
}
17511757
}
17521758
for (int i = 0; i <= indirect; ++i)

Diff for: test/cfg/std.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -5053,3 +5053,20 @@ void assertWithSideEffect_std_prev_next(const std::vector<int>& v, std::vector<i
50535053
// cppcheck-suppress checkLibraryNoReturn
50545054
assert(std::next(it, 1) == v.end());
50555055
}
5056+
5057+
std::vector<int> containerOutOfBounds_push_back() { // #12775
5058+
std::vector<int> v;
5059+
for (int i = 0; i < 4; ++i) {
5060+
v.push_back(i);
5061+
(void)v[i];
5062+
}
5063+
return v;
5064+
}
5065+
5066+
template <typename T>
5067+
void constVariablePointer_push_back(std::vector<T*>& d, const std::vector<T*>& s) {
5068+
for (const auto& e : s) {
5069+
T* newE = new T(*e);
5070+
d.push_back(newE);
5071+
}
5072+
}

0 commit comments

Comments
 (0)