Skip to content

C++: Round towards +/- Inf in range analysis#374

Merged
semmle-qlci merged 4 commits into
github:masterfrom
jbj:range-analysis-rounding
Oct 29, 2018
Merged

C++: Round towards +/- Inf in range analysis#374
semmle-qlci merged 4 commits into
github:masterfrom
jbj:range-analysis-rounding

Conversation

@jonas-semmle

Copy link
Copy Markdown
Contributor

This PR attempts to fix a long-standing problem with the range analysis. It doesn't handle 64-bit numbers correctly because we’re using double-precision floats for the arithmetic internally, and when we add 0xffff_ffff_ffff_ffff + 1 as doubles we get 0xffff_ffff_ffff_ffff. This makes the analysis believe the numbers can’t wrap around.

The problem is tracked in ODASA-7205 and was most recently reported on the lgtm.com forum by @pm215. The last attempt to fix this was the internal PR 26489 by @kevinbackhouse, which was abandoned due to a lack of time to investigate (1) performance implications and (2) a test failure caused by how the new rounding interacts with negative zero.

The first two commits are ports of PR 26489 to the open QL repository. Commit 3 factors out similar code, and commit 4 fixes the treatment of negative zero. I've tested the changes for performance on LibreOffice, ChakraCore, Blender and p3/regal, and I've found any slowdown to be within the noise level of our performance measurements. I've tested for correctness on LibreOffice, ChakraCore and Blender, and I found two FPs removed and two TPs removed. The removed TPs are of the following form, where we can't establish that my64bitnumber is small enough.

for (int i = 1; i < my64bitnumber; i++) {
  ... i == 0 ...
}

@jonas-semmle

Copy link
Copy Markdown
Contributor Author

This test case is an example of the good results that go away with this change:

int overeager_wraparound(unsigned int u32bound, unsigned long long u64bound) {
  unsigned int u32idx;
  unsigned long long u64idx;

  for (u32idx = 1; u32idx < u32bound; u32idx++) {
    if (u32idx == 0) // NO ALERT BEFORE OR AFTER
      return 0;
  }

  for (u64idx = 1; u64idx < u64bound; u64idx++) {
    if (u64idx == 0) // ALERT BEFORE, NO ALERT AFTER
      return 0;
  }

  return 1;
}

Notice that the 32-bit case is wrong independently of this PR, so this PR just changes the 64-bit case to be as wrong as the 32-bit case.

bindingset[x]
private float normalizeFloatUp(float x) {
result = x + 0.0
}

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.

It would be better if we added IEEE comparison as a method of float, but this looks like it will work for now.

@semmle-qlci semmle-qlci merged commit 7d37cf4 into github:master Oct 29, 2018
aibaars added a commit that referenced this pull request Oct 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants