This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Treat values passed as parameter as escaped
Needs ReviewPublic

Authored by t-8ch on Aug 3 2022, 4:31 AM.

Details

Summary

This fixes false-positives when a function or constructor takes address of the
parameter.

The motivation behind this change is a custom class that works similar to
std::string_view that takes the length by reference from an existing variable.
clang-tidy reported stores to the original length variable as dead although
they were actually observed by the string view.

Diff Detail

Event Timeline

t-8ch created this revision.Aug 3 2022, 4:31 AM
Herald added a project: Restricted Project. · View Herald Transcript
t-8ch requested review of this revision.Aug 3 2022, 4:31 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 3 2022, 4:31 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Thanks, looks good with some nits.

clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
571–573

This could be hoisted into findCallReferenceParameters(const FunctionDecl *FD) thus avoiding code repetition.

t-8ch updated this revision to Diff 449934.Aug 4 2022, 4:59 AM

Hoisted common code into common function.

martong accepted this revision.Aug 4 2022, 5:10 AM

LGTM

This revision is now accepted and ready to land.Aug 4 2022, 5:10 AM

Please, consider stating the motivation behind this change.
For me, by looking at the modified test, it feels like we would lose legitimate findings (aka. true-positive reports) by applying this change.
From my perspective, the store to i is dead, since it will be immediately overwritten in the referenceParameters() function.

clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
551–552

You don't seem to use the index, only for iterating over the parameters.
I believe, FD->parameters() would simplify this code.

553–560

By looking at e.g. findLambdaReferenceCaptures(), it seems like we should prefer continue statements for filtering; probably to avoid such deeply nested intendations.
Along with that, I think the llvm style guide prefers not wrapping single statement branches by courly braces.

564

I'm not sure getCalleeDecl() always returns a valid pointer.
I believe the dyn_cast_or_null would be more appropriate in this case.
This theory is underpinned by the other uses of the getCalleeDecl() API.

570

Why don't you use the CE->arguments() method instead? That would result in a range (begin + end).
I believe that is superior in most cases.

steakhal requested changes to this revision.Aug 5 2022, 3:28 AM
This revision now requires changes to proceed.Aug 5 2022, 3:28 AM
steakhal added inline comments.Aug 5 2022, 4:49 AM
clang/test/Analysis/dead-stores.cpp
243

By checking it on godbolt, this line never raised a warning.
https://godbolt.org/z/n7fP5od5q

You must be referring to L230 and L241 instead.

t-8ch updated this revision to Diff 450717.Aug 8 2022, 12:44 AM
t-8ch marked 5 inline comments as done.

Implement stylistic change requested by reviewers and add better description.

t-8ch added a comment.Aug 8 2022, 12:57 AM

@steakhal
Thanks for your review!
I think I have addressed all your points.

Please, consider stating the motivation behind this change.

This is now added.

For me, by looking at the modified test, it feels like we would lose legitimate findings (aka. true-positive reports) by applying this change.

The change will indeed loose legitimate findings. We would have to also analyze the source code of the called function to see if it takes the address of the parameter.
But for callees which we don't have access to the AST this would still not work.

The existing escape-analysis works the same way, marking anything as escaped that *could* have escaped.
See the existing test basicLambda or the non-reference example:

void foo() {
  int x = 0;
  if (&x != &x) {
    return;
  }
  // This is clearly a dead store but as we have taken the address of x above it is treated as escaped.
  x = 3;
}

From my perspective, the store to i is dead, since it will be immediately overwritten in the referenceParameters() function.

NoQ added a comment.Aug 8 2022, 12:28 PM

Thanks! I've seen a couple false positives like this and I'm shocked that this wasn't already how things worked.

I agree with @steakhal, we should add a test case that demonstrates an actual false positive suppressed by this patch. The tests that you've added are "FIXME" tests that demonstrate regressions caused by this patch - which is great and we should totally keep them! - but we should add a comment saying that in ideal world, if this checker's analysis was interprocedural, we would have noticed that this is still a dead store. But the purpose of tests is to avoid regressions, so it's important to add a test that demonstrates the improvement as well, so that this improvement wasn't lost. If we don't have improvements tested, somebody may remove your code and say "Hey, I haven't regressed anything and I even improved behavior on some tests!".

t-8ch updated this revision to Diff 451072.Aug 9 2022, 2:24 AM

Rework tests

steakhal accepted this revision.Aug 9 2022, 4:34 AM

Please, consider stating the motivation behind this change.

This is now added.

I was about the empty revision summary.
Generally, we use that to highlight the motivation of some proposed change.
We also tend to keep it in sync which the commit message.
(I think arcanist auto updates the review version of the summary to match with the actual message once you commit your change.)

Sorry for blocking this change. It makes sense now.

clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
552

I think as we are on C++17 now, we could use structured bindings. WDYT?

This revision is now accepted and ready to land.Aug 9 2022, 4:34 AM
t-8ch edited the summary of this revision. (Show Details)Aug 9 2022, 4:38 AM
t-8ch added a comment.Aug 9 2022, 4:43 AM
clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
552

Sounds good, will update.
Note that "now" means since three days ago :-)

t-8ch updated this revision to Diff 451117.Aug 9 2022, 5:17 AM
  • Inline tiny functions
  • Use structured binding
  • Rename findReferenceParameter -> findReferenceParameters
t-8ch requested review of this revision.Sep 11 2022, 2:26 PM
NoQ added inline comments.Sep 20 2022, 5:45 PM
clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
552

IIRC there are a few cases where parameters and arguments don't zip together nicely (cf. the hacks we needed to do in CXXMemberOperatorCall::getAdjustedParameterIndex(), CXXMemberOperatorCall::::getASTArgumentIndex()).

t-8ch updated this revision to Diff 461799.Sep 20 2022, 11:52 PM
  • [analyzer] Treat values passed as parameter as escaped
  • [analyzer][deadstores] Handle member operator calls

Handle calls to member operators

t-8ch added inline comments.Sep 20 2022, 11:56 PM
clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
552

Indeed. I added logic to handle it.
Unfortunately I didn't see how to use CXXMemberOperatorCall directly.
So I extracted the detection logic from CallEventManager::getSimpleCall() into isMemberOperatorCall().

Any hints on how to do this better are much appreciated.