Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I fix wrong numbers produced by integer overflow?

I had a bug that caused an integer overflow, resulting in wrong (negative) timestamps being written to the database. The code is fixed already, but I want to fix the wrong data, too.

I thought, I could just take the wrong results and add Integer.MAX_VALUE, but that didn't seem to work, it left me with to high values. I have the offset value in the code snippet below, but the input values are not stored.

The following code reproduces the bug:

@Test
public void testArexxConversion()
{
    // The input values represent seconds since midnight, Jan 1, 2000 UTC
    final int sample = 361450072; // A sample input value drawn from production
    // I use the offset from the UNIX epoch to convert the vakue to UNIX seconds
    final int offset = 946684800; // midnight, Jan 01 2000 UTC in UNIX seconds
    // This was the buggy line in my code, the assertion will fail
    long result = (sample + offset) * 1000;
    // Prints 'Result is negative: -1830153280'
    Assert.assertTrue(result > 0, String.format("Result is negative: %d", result));
    // This is for comparison
    Date dt = new Date(offset * 1000);
    Assert.assertEquals(dt.getTime() + sample * 1000, result);
}
like image 548
Hanno Fietz Avatar asked Jun 15 '11 11:06

Hanno Fietz


1 Answers

How to fix the bug in your database

To fix the bug in your database you can do the following addition to all the buggy data:

long new_result = old_buggy_result + 1309965025280L;

The constant number was found like this:

  1. Check the buggy result value
  2. Find what should the correct result value be?
  3. Do an addition to the buggy result value to find the correct `result.

But this is only possible if you have saved sample and offset in your database or somewhere else.

Otherwise, it depends on the number of wraps that occured during the original calculation:

long size_of_int = (long)Math.pow(2, 32);
int number_of_wraps = 305 // Only correct in your example!
                          // You can't deduct the number of wraps from
                          // the wrong value alone, because that information
                          // is lost in the modulo (the "wrap")
long correct_number = wrong_number + size_of_int * number_of_wraps;

If the numbers in your database are close enough to your sample value, this means, you can do the above, using 305 as the number of wraps.

Explanation of the bug (for future readers)

The operation here:

 (sample + offset) * 1000;

is computed using int and not long. But the result is "too big" to be saved on an int variable. That's why you have an overflow.

Change it to:

  ((long) sample + offset) * 1000L;

So now the + and * operations will be done using long values, and the result will be a long value which won't overflow.

like image 111
insumity Avatar answered Sep 29 '22 13:09

insumity