Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ion_int_from_long never returns when MIN_INT64 is passed #224

Closed
zslayton opened this issue Feb 4, 2021 · 1 comment
Closed

ion_int_from_long never returns when MIN_INT64 is passed #224

zslayton opened this issue Feb 4, 2021 · 1 comment

Comments

@zslayton
Copy link
Contributor

zslayton commented Feb 4, 2021

In the following code, the call to ion_int_from_long(&iint, MIN_INT64) will never return:

    // ...
    hWRITER writer;
    ion_writer_open_buffer(&writer, (BYTE *) buffer, (SIZE) 2048, &options);
    ION_INT iint;
    ion_int_init(&iint, &writer);
    ion_int_from_long(&iint, MIN_INT64);

When ion_int_from_long sets out to calculate the magnitude (i.e. the absolute value) of the provided value, it checks to see if the value is negative and if so, negates it:

    if ((is_neg = (value < 0)) == TRUE) {
        value = -value;
    }

This code fails when the provided value is MIN_INT64.

MIN_INT64 is defined here:

#define MAX_INT64  0x7FFFFFFFFFFFFFFFLL
#define MIN_INT64  -0x7FFFFFFFFFFFFFFFLL-1

MAX_INT64 is the largest value that can be stored in a signed 64-bit integer, int64_t. Notice that MIN_INT64's magnitude is one greater than that of MAX_INT64. This means that the absolute value of MIN_INT64 is too large to fit in an int64_t. Attempting to evaluate -MIN_INT64 or abs(MIN_INT64 is undefined behavior. In my build environment, the compiler treats it as a no-op.

In this corner case, value = -value does nothing and the following loop performs a series of right shifts on a value that (unexpectedly) still has its sign bit set to 1:

    ii_length = 0; 
    temp = value;
    while (temp) {
        temp >>= II_SHIFT; // <-- With the sign bit set, `temp` will never reach zero from right-shifting.
        ii_length++;
    }

Under normal circumstances, each right shift operation would drive temp closer to zero, ultimately ending the loop. However, with the sign bit set, each right shift populates the most significant bits in temp with 1s, leading to an infinite loop.

zslayton added a commit that referenced this issue Feb 4, 2021
Addressed a corner case in ion_int_from_long that caused an
infinite loop when MIN_INT64 was passed in as the long value.
zslayton added a commit that referenced this issue Feb 4, 2021
Addressed a corner case in ion_int_from_long that caused an
infinite loop when MIN_INT64 was passed in as the long value.
zslayton added a commit that referenced this issue Feb 9, 2021
* Fixed issues #224 & #226: Ion int to/from int64_t

* Addressed a corner case in ion_int_from_long that caused an
infinite loop when MIN_INT64 was passed in as the long value.
* Modified the `ion_int_to_int64` function to work on 63- and
  64-bit magnitudes.
* Adds roundtripping unit tests that convert an int64_t to an
  IINT and then back to an int64_t again.
* Added a unit test to verify that `ion_int_to_int64` detects
  overflow as expected. It tests both the case where the value
  can fit in 64 unsigned bits (but not 64 signed bits) and the
  case where the number is too big to fit in 64 bits.

Co-authored-by: Zack Slayton <[email protected]>
@zslayton
Copy link
Contributor Author

zslayton commented Feb 9, 2021

Fixed by PR #225.

@zslayton zslayton closed this as completed Feb 9, 2021
rgantt pushed a commit to rgantt/ion-c that referenced this issue Mar 2, 2021
* Fixed issues amazon-ion#224 & amazon-ion#226: Ion int to/from int64_t

* Addressed a corner case in ion_int_from_long that caused an
infinite loop when MIN_INT64 was passed in as the long value.
* Modified the `ion_int_to_int64` function to work on 63- and
  64-bit magnitudes.
* Adds roundtripping unit tests that convert an int64_t to an
  IINT and then back to an int64_t again.
* Added a unit test to verify that `ion_int_to_int64` detects
  overflow as expected. It tests both the case where the value
  can fit in 64 unsigned bits (but not 64 signed bits) and the
  case where the number is too big to fit in 64 bits.

Co-authored-by: Zack Slayton <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant