Fix 11415: FP containerOutOfBounds for container initialized in virtual method (#4622)

This commit is contained in:
Paul Fultz II 2022-12-08 13:10:58 -06:00 committed by GitHub
parent 04b7c0c200
commit 0cb742701d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 13 deletions

View File

@ -1349,7 +1349,7 @@ static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const
if (child->exprId() > 0 && pm.hasValue(child->exprId())) { if (child->exprId() > 0 && pm.hasValue(child->exprId())) {
ValueFlow::Value& v = pm.at(child->exprId()); ValueFlow::Value& v = pm.at(child->exprId());
if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) {
if (isContainerSizeChanged(child, settings)) if (isContainerSizeChanged(child, v.indirect, settings))
v = unknown; v = unknown;
} else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) {
if (isVariableChanged(child, v.indirect, settings, true)) if (isVariableChanged(child, v.indirect, settings, true))

View File

@ -7540,10 +7540,14 @@ static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDataba
static bool isContainerSizeChanged(nonneg int varId, static bool isContainerSizeChanged(nonneg int varId,
const Token* start, const Token* start,
const Token* end, const Token* end,
int indirect,
const Settings* settings = nullptr, const Settings* settings = nullptr,
int depth = 20); int depth = 20);
static bool isContainerSizeChangedByFunction(const Token* tok, const Settings* settings = nullptr, int depth = 20) static bool isContainerSizeChangedByFunction(const Token* tok,
int indirect,
const Settings* settings = nullptr,
int depth = 20)
{ {
if (!tok->valueType()) if (!tok->valueType())
return false; return false;
@ -7566,12 +7570,13 @@ static bool isContainerSizeChangedByFunction(const Token* tok, const Settings* s
if (!ftok) if (!ftok)
return false; // not a function => variable not changed return false; // not a function => variable not changed
const Function * fun = ftok->function(); const Function * fun = ftok->function();
if (fun && !fun->hasVirtualSpecifier()) { if (fun && !fun->isImplicitlyVirtual()) {
const Variable *arg = fun->getArgumentVar(narg); const Variable *arg = fun->getArgumentVar(narg);
if (arg) { if (arg) {
if (!arg->isReference() && !addressOf) const bool isPointer = addressOf || indirect > 0;
if (!arg->isReference() && !isPointer)
return false; return false;
if (!addressOf && arg->isConst()) if (!isPointer && arg->isConst())
return false; return false;
if (arg->valueType() && arg->valueType()->constness == 1) if (arg->valueType() && arg->valueType()->constness == 1)
return false; return false;
@ -7581,8 +7586,12 @@ static bool isContainerSizeChangedByFunction(const Token* tok, const Settings* s
if (!arg->nameToken()) if (!arg->nameToken())
return false; return false;
if (depth > 0) if (depth > 0)
return isContainerSizeChanged( return isContainerSizeChanged(arg->declarationId(),
arg->declarationId(), scope->bodyStart, scope->bodyEnd, settings, depth - 1); scope->bodyStart,
scope->bodyEnd,
addressOf ? indirect + 1 : indirect,
settings,
depth - 1);
} }
// Don't know => Safe guess // Don't know => Safe guess
return true; return true;
@ -7590,7 +7599,7 @@ static bool isContainerSizeChangedByFunction(const Token* tok, const Settings* s
} }
bool inconclusive = false; bool inconclusive = false;
const bool isChanged = isVariableChangedByFunctionCall(tok, 0, settings, &inconclusive); const bool isChanged = isVariableChangedByFunctionCall(tok, indirect, settings, &inconclusive);
return (isChanged || inconclusive); return (isChanged || inconclusive);
} }
@ -7685,7 +7694,7 @@ struct ContainerExpressionAnalyzer : ExpressionAnalyzer {
return Action::Invalid; return Action::Invalid;
if (isLikelyStreamRead(isCPP(), tok->astParent())) if (isLikelyStreamRead(isCPP(), tok->astParent()))
return Action::Invalid; return Action::Invalid;
if (astIsContainer(tok) && isContainerSizeChanged(tok, getSettings())) if (astIsContainer(tok) && isContainerSizeChanged(tok, getIndirect(tok), getSettings()))
return read | Action::Invalid; return read | Action::Invalid;
return read; return read;
} }
@ -7787,7 +7796,7 @@ ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, const ValueFlow::Va
return ExpressionAnalyzer(exprTok, value, tokenlist); return ExpressionAnalyzer(exprTok, value, tokenlist);
} }
bool isContainerSizeChanged(const Token* tok, const Settings* settings, int depth) bool isContainerSizeChanged(const Token* tok, int indirect, const Settings* settings, int depth)
{ {
if (!tok) if (!tok)
return false; return false;
@ -7823,7 +7832,7 @@ bool isContainerSizeChanged(const Token* tok, const Settings* settings, int dept
case Library::Container::Action::CHANGE_INTERNAL: case Library::Container::Action::CHANGE_INTERNAL:
break; break;
} }
if (isContainerSizeChangedByFunction(tok, settings, depth)) if (isContainerSizeChangedByFunction(tok, indirect, settings, depth))
return true; return true;
return false; return false;
} }
@ -7831,13 +7840,14 @@ bool isContainerSizeChanged(const Token* tok, const Settings* settings, int dept
static bool isContainerSizeChanged(nonneg int varId, static bool isContainerSizeChanged(nonneg int varId,
const Token* start, const Token* start,
const Token* end, const Token* end,
int indirect,
const Settings* settings, const Settings* settings,
int depth) int depth)
{ {
for (const Token *tok = start; tok != end; tok = tok->next()) { for (const Token *tok = start; tok != end; tok = tok->next()) {
if (tok->varId() != varId) if (tok->varId() != varId)
continue; continue;
if (isContainerSizeChanged(tok, settings, depth)) if (isContainerSizeChanged(tok, indirect, settings, depth))
return true; return true;
} }
return false; return false;

View File

@ -467,7 +467,7 @@ namespace ValueFlow {
ValueFlow::Value asImpossible(ValueFlow::Value v); ValueFlow::Value asImpossible(ValueFlow::Value v);
bool isContainerSizeChanged(const Token* tok, const Settings* settings = nullptr, int depth = 20); bool isContainerSizeChanged(const Token* tok, int indirect, const Settings* settings = nullptr, int depth = 20);
struct LifetimeToken { struct LifetimeToken {
const Token* token; const Token* token;

View File

@ -867,6 +867,25 @@ private:
" return m.at(1);\n" " return m.at(1);\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkNormal("struct A {\n"
" virtual void init_v(std::vector<int> *v) = 0;\n"
"};\n"
"A* create_a();\n"
"struct B {\n"
" B() : a(create_a()) {}\n"
" void init_v(std::vector<int> *v) {\n"
" a->init_v(v);\n"
" }\n"
" A* a;\n"
"};\n"
"void f() {\n"
" B b;\n"
" std::vector<int> v;\n"
" b.init_v(&v);\n"
" v[0];\n"
"}\n");
ASSERT_EQUALS("", errout.str());
} }
void outOfBoundsSymbolic() void outOfBoundsSymbolic()