Bug #21436
closedDate#ajd returns incorrect positive values due to integer overflow for large negative years
Description
Date#ajd
(astronomical Julian Day number) returns incorrect positive values instead of negative values for certain large negative years due to signed integer overflow in the RB_INT2FIX
function.
require 'date'
# Works correctly
Date.civil(-110823815392979, 2, 19).ajd
#=> (-80956797141128945/2)
# BUG - returns positive instead of negative
Date.civil(-11082381539297990, 2, 19).ajd
#=> (1127692322401036327/2) # Should be negative!
# Works correctly (takes a different code path)
Date.civil(-111111111082381539297990, 2, 19).ajd
#=> (-81166666645679714453739481/2)
In ext/date/date_core.c, the ir
value can overflow
if (FIXNUM_P(r) && FIX2LONG(r) <= (FIXNUM_MAX / 2)) {
long ir = FIX2LONG(r);
ir = ir * 2 - 1;
// On Date.civil(-11082381539297990, 2, 19).ajd
//
// ir = -8095679714453739481
return rb_rational_new2(LONG2FIX(ir), INT2FIX(2)); // Overflow in LONG2FIX
}
Then in include/ruby/internal/arithmetic/long.h (LONG2FIX
is an alias for RB_INT2FIX
)
static inline VALUE
RB_INT2FIX(long i)
{
RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(i));
/* :NOTE: VALUE can be wider than long. As j being unsigned, 2j+1 is fully
* defined. Also it can be compiled into a single LEA instruction. */
const unsigned long j = RBIMPL_CAST((unsigned long)i);
const unsigned long k = (j << 1) + RUBY_FIXNUM_FLAG;
const long l = RBIMPL_CAST((long)k);
const SIGNED_VALUE m = l; /* Sign extend */
const VALUE n = RBIMPL_CAST((VALUE)m);
RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(n));
return n;
}
It effectively goes:
j = (unsigned long)-8095679714453739481 // 10351064359255812135
k = (10351064359255812135 << 1) + RUBY_FIXNUM_FLAG // 2255384644802072655
...
So, 2255384644802072655
encodes 1127692322401036327
, which is the numerator for the observed ajd
return value in the reproducer above.
It also affects Date
comparisons (since ajd
is used in cmp_gen
)
Updated by Anonymous about 1 month ago
- Status changed from Open to Closed
Applied in changeset git|022c18b60d2245980abcdd7b5195eebca73b8809.
[ruby/date] [Bug #21436] check for fixnum lower bound in m_ajd
Issue - https://bugs.ruby-lang.org/issues/21436
Apparently, the lower bound check is missing, which results in overflow & wrapping later on in RB_INT2FIX
Signed-off-by: Dmitry Dygalo [email protected]
https://github.com/ruby/date/commit/67d75e8423