Skip to content

Commit 7524d7c

Browse files
committed
Range-check the integral Rational offset
Every branch of offset_to_sec range-checks the resulting number of seconds except one path through the Rational branch: when the day fraction is an integral Rational, n is assigned inside the if arm and reaches *rof without passing the guard that sits in the else arm. DateTime.new(2024, 1, 1, 0, 0, 0, Rational(2, 1)) therefore produced a 48-hour offset, while the equivalent Integer 2 is rejected and falls back to +00:00. Rational(49710, 1) is 4_294_944_000 seconds, over INT_MAX, so the (int) narrowing turned a large positive offset into a negative one. Move the check below the if/else so it covers both arms. That also bounds n before the narrowing. Rational(1, 1) is exactly DAY_IN_SECONDS and the guard is inclusive, so in-range values are unaffected.
1 parent 83eb9d4 commit 7524d7c

2 files changed

Lines changed: 17 additions & 2 deletions

File tree

ext/date/date_core.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,9 +2621,9 @@ offset_to_sec(VALUE vof, int *rof)
26212621
if (!FIXNUM_P(vn))
26222622
return 0;
26232623
n = FIX2LONG(vn);
2624-
if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS)
2625-
return 0;
26262624
}
2625+
if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS)
2626+
return 0;
26272627
*rof = (int)n;
26282628
return 1;
26292629
}

test/date/test_date_new.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,21 @@ def test_civil__ex
192192
end
193193
end
194194

195+
def test_civil__offset
196+
d = DateTime.civil(2001,2,3, 0,0,0, Rational(1, 1))
197+
assert_equal(1.to_r, d.offset)
198+
d = DateTime.civil(2001,2,3, 0,0,0, Rational(-1, 1))
199+
assert_equal(-1.to_r, d.offset)
200+
201+
# An out-of-range offset is ignored, as it is for the equivalent Integer.
202+
d = DateTime.civil(2001,2,3, 0,0,0, 2)
203+
assert_equal(0, d.offset)
204+
d = DateTime.civil(2001,2,3, 0,0,0, Rational(2, 1))
205+
assert_equal(0, d.offset)
206+
d = DateTime.civil(2001,2,3, 0,0,0, Rational(49710, 1))
207+
assert_equal(0, d.offset)
208+
end
209+
195210
def test_civil__reform
196211
d = Date.jd(Date::ENGLAND, Date::ENGLAND)
197212
dt = DateTime.jd(Date::ENGLAND, 0,0,0,0, Date::ENGLAND)

0 commit comments

Comments
 (0)