Skip to content

C++: Fix false positive in cpp/comparison-with-wider-type#5859

Merged
MathiasVP merged 7 commits intogithub:mainfrom
MathiasVP:fix-fp-in-comparison-with-wider-type
May 10, 2021
Merged

C++: Fix false positive in cpp/comparison-with-wider-type#5859
MathiasVP merged 7 commits intogithub:mainfrom
MathiasVP:fix-fp-in-comparison-with-wider-type

Conversation

@MathiasVP
Copy link
Contributor

Fixes #5850.

Prior to this PR, we ignored the conversions on the large expression when computing range analysis.

@MathiasVP MathiasVP added the C++ label May 10, 2021
@MathiasVP MathiasVP requested a review from a team as a code owner May 10, 2021 08:15
geoffw0
geoffw0 previously approved these changes May 10, 2021
Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

LGTM.

@@ -0,0 +1,3 @@
void test_issue_5850(unsigned char small, unsigned int large1) {
for(; small < static_cast<unsigned char>(large1 - 1); small++) { } // GOOD
Copy link
Contributor

Choose a reason for hiding this comment

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

I've convinced myself I'm happy with this. There could still be a correctness issue if large1 - 1 overflows an unsigned char causing the loop to end at an unexpected point, but (1) the programmer has explicitly made the cast, suggesting they are happy it fits and (2) regardless the loop will terminate (which it might not without the cast), and I remember that being the main concern here.

Copy link
Contributor Author

@MathiasVP MathiasVP May 10, 2021

Choose a reason for hiding this comment

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

Good point. Would you be happier with the change if we only did large.getExplicitlyConverted() instead of large.getFullyConverted()? That would only include the conversions the programmer actually wrote.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'm happy with getFullyConverted ... LGTM diff to be sure: https://lgtm.com/query/3711128432146950375/

Copy link
Contributor

Choose a reason for hiding this comment

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

^ almost all results are the same for all three versions; I found just three that were excluded by the conversion bit (either getFullyConverted or getExplicitlyConverted) and they all resembled the motivating case for this change.

So from a practical point of view I'm happy with this change as-is, but it sounds like @jbj has spotted a theoretical concern worthy of discussion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I'm trying to see if I can produce a testcase that has the problem. Let's hold off with the merge until we have a conclusion on that.

Copy link
Contributor

Choose a reason for hiding this comment

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

It makes sense that fully and explicitly converted are the same on large. The query is about a small type compared to a large type, so the implicit conversions will (always?) happen on the small side of the comparison.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, yes. That's a good point!

Copy link
Contributor

Choose a reason for hiding this comment

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

Unlikely to be found like this in the wild, but maybe:

void test(unsigned char small, unsigned int large1) {
  for(; small < (unsigned int)(unsigned char)(large1 - 1); small++) { } // GOOD
}

@jbj jbj self-requested a review May 10, 2021 11:51
large = rel.getGreaterOperand() and
rel = l.getCondition().getAChild*() and
upperBound(large).log2() > getComparisonSize(small) * 8 and
upperBound(large.getFullyConverted()).log2() > getComparisonSize(small) * 8 and
Copy link
Contributor

Choose a reason for hiding this comment

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

I worry that this change could lead to false positives when the conversion is the other way around, widening rather than narrowing. If the range analysis can't determine that a signed char x is non-negative, then (unsigned int)x will have an upper bound of INT_MAX. I might be wrong, so please add a test to confirm.

To be on the safe side, you could add the new line of code rather than replacing what was there.

Copy link
Contributor

Choose a reason for hiding this comment

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

or

forall(Expr conv | conv = large.getConversion*() | upperBound(conv).log2() > getComparisonSize(small) * 8) and

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I have no principled argument for why the endpoints of the conversion should be special. But with both forall and getConversion* we'd have to check for performance regressions more carefully.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I'm in favor of @geoffw0's more general solution as well. I'll add a problematic testcase first (the naive one I made didn't reveal any false positives, but I think I can tweak it so that it does the wrong thing), and see how the forall performs.

@MathiasVP
Copy link
Contributor Author

@geoffw0 @jbj I've added a new false positive in c7cd754 and fixed it with @geoffw0's idea in c0b6531. Performance looks fine on Wireshark. LGTM shows a couple of removed results: https://lgtm.com/query/9135193735162628855/

geoffw0
geoffw0 previously approved these changes May 10, 2021
Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

Changes and diffs LGTM.

Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

Good catch, still LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LGTM.com - false positive

3 participants