Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions lib/vf_analyzers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ struct ValueFlowAnalyzer : Analyzer {
} else if (isDereferenceOp(ref) && !match(ref->astOperand1())) {
const Token* lifeTok = nullptr;
for (const ValueFlow::Value& v:ref->astOperand1()->values()) {
if (!v.isLocalLifetimeValue())
if (!v.isLocalLifetimeValue() && !v.isSubFunctionLifetimeValue())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of skipping subfunction lifetimes, you could just check that the path is the same if the propagated value's path(ie the value returned from getValue(lifetok)) is not zero.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subfunction lifetimes were skipped before this change, and we didn't proceed to analyzeLifetime() below.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I read it backwards. Its enabling it. We still need to check the correct path to avoid FPs.

continue;
if (v.conditional)
continue;
Expand Down Expand Up @@ -1072,7 +1072,12 @@ struct MultiValueFlowAnalyzer : ValueFlowAnalyzer {
}

bool match(const Token* tok) const override {
return values.count(tok->varId()) > 0;
if (tok->varId() == 0)
Comment thread Dismissed
return false;
return values.count(tok->varId()) > 0 ||
std::any_of(values.begin(), values.end(), [&](const std::pair<nonneg int, ValueFlow::Value>& p) {
return p.second.isUninitValue() && p.second.tokvalue->varId() == tok->varId();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this extra check needed?

Copy link
Copy Markdown
Collaborator Author

@chrchr-github chrchr-github Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which check exactly? We now match in case there is an alias that has an uninit value pointing to the variable in question. But I'm not sure if it is correct to only check for uninit values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should happen in the analyzeToken function, not here. We are already checking for aliases there as well.

Finally the tokvalue is not always an alias either.

Copy link
Copy Markdown
Collaborator Author

@chrchr-github chrchr-github Jan 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

analyzeToken() has this:

Action la = analyzeLifetime(lifeTok);
if (la.matches()) { ...

So matches() needs to return true for the propagation of the uninit value to stop.
I looked at how that works for local code and tried to make the same stuff happen with a subfunction/valueFlowInjectParameter()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So matches() needs to return true for the propagation of the uninit value to stop.

Its not that matches() needs to return true, but that analyzeLifetime should return Action::Match, but I also think it can return Action::Read as well as the isAliasModified will handle it for cases where its not an exact match.

So it seems like you are modifying match so analyzeLifetime returns Action::Match, which is not the right approach. The match function is for exact matches, not to match aliases. That can lead to FPs. This needs to be done in either analyzeToken or analyzeLifetime where we are already checking for aliases.

}

ProgramState getProgramState() const override {
Expand Down
11 changes: 11 additions & 0 deletions test/testuninitvar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4461,6 +4461,17 @@ class TestUninitVar : public TestFixture {
" return f(i, 0);\n"
"}");
ASSERT_EQUALS("[test.cpp:8:14] -> [test.cpp:4:12]: (warning) Uninitialized variable: i [uninitvar]\n", errout_str());

valueFlowUninit("char *f (char *b) {\n" // #12612
" char* p = b;\n"
" *p = '\\0';\n"
" return b;\n"
"}\n"
"void g() {\n"
" char a[24];\n"
" f(a);\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}

void uninitStructMember() { // struct members
Expand Down
Loading