From 89fce7e211fd42be8a6c494ad1b89a8478ab5ffd Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 11 Jun 2025 23:29:41 +0900 Subject: [PATCH 1/3] The device numbers in `struct statx` may be larger than `dev_t` `dev_t` is already 64-bit in glibc, but on some platforms like Alpine Linux and Android NDK, `makedev` is defined as more than 32-bit ( promoting to `unsigned long long` then left-shifting by 32bit), while `dev_t` is still 32-bit. --- file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/file.c b/file.c index 322df6dbec896c..936e0cdb9564fa 100644 --- a/file.c +++ b/file.c @@ -662,7 +662,7 @@ rb_stat_dev(VALUE self) #if RUBY_USE_STATX unsigned int m = get_stat(self)->stx_dev_major; unsigned int n = get_stat(self)->stx_dev_minor; - return DEVT2NUM(makedev(m, n)); + return ULL2NUM(makedev(m, n)); #elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_DEV_T return DEVT2NUM(get_stat(self)->st_dev); #elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_LONG @@ -833,7 +833,7 @@ rb_stat_rdev(VALUE self) #if RUBY_USE_STATX unsigned int m = get_stat(self)->stx_rdev_major; unsigned int n = get_stat(self)->stx_rdev_minor; - return DEVT2NUM(makedev(m, n)); + return ULL2NUM(makedev(m, n)); #elif !defined(HAVE_STRUCT_STAT_ST_RDEV) return Qnil; #elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_DEV_T From a61aa604948c9299270d526eaa2da588cec48169 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 11 Jun 2025 23:33:14 +0900 Subject: [PATCH 2/3] Conversion is needed between `WIDEVALUE` and `VALUE` --- time.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/time.c b/time.c index 1b02cf425905cf..8238ea574bc81a 100644 --- a/time.c +++ b/time.c @@ -1891,7 +1891,7 @@ time_mark(void *ptr) { struct time_object *tobj = ptr; if (!FIXWV_P(tobj->timew)) { - rb_gc_mark_movable(WIDEVAL_GET(tobj->timew)); + rb_gc_mark_movable(w2v(tobj->timew)); } rb_gc_mark_movable(tobj->vtm.year); rb_gc_mark_movable(tobj->vtm.subsecx); @@ -1904,7 +1904,7 @@ time_compact(void *ptr) { struct time_object *tobj = ptr; if (!FIXWV_P(tobj->timew)) { - WIDEVAL_GET(tobj->timew) = rb_gc_location(WIDEVAL_GET(tobj->timew)); + WIDEVAL_GET(tobj->timew) = WIDEVAL_WRAP(rb_gc_location(w2v(tobj->timew))); } tobj->vtm.year = rb_gc_location(tobj->vtm.year); From d67faf1cf51438afc918d94e40a2cd654f7c1df2 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 13 Jun 2025 18:25:14 +0900 Subject: [PATCH 3/3] Normalize subseconds using `wideint_t` instead of `time_t` --- time.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/time.c b/time.c index 8238ea574bc81a..0e91521db1a8d7 100644 --- a/time.c +++ b/time.c @@ -249,6 +249,7 @@ divmodv(VALUE n, VALUE d, VALUE *q, VALUE *r) # define FIXWV2WINT(w) FIX2LONG(WIDEVAL_GET(w)) #endif +#define SIZEOF_WIDEINT SIZEOF_INT64_T #define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1) #define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN) #define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w)) @@ -1968,11 +1969,11 @@ time_modify(VALUE time) } static wideval_t -timenano2timew(time_t sec, long nsec) +timenano2timew(wideint_t sec, long nsec) { wideval_t timew; - timew = rb_time_magnify(TIMET2WV(sec)); + timew = rb_time_magnify(WINT2WV(sec)); if (nsec) timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000)); return timew; @@ -2747,15 +2748,15 @@ time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone, V } static void -subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec) +subsec_normalize(wideint_t *secp, long *subsecp, const long maxsubsec) { - time_t sec = *secp; + wideint_t sec = *secp; long subsec = *subsecp; long sec2; if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */ sec2 = subsec / maxsubsec; - if (TIMET_MAX - sec2 < sec) { + if (WIDEINT_MAX - sec2 < sec) { rb_raise(rb_eRangeError, "out of Time range"); } subsec -= sec2 * maxsubsec; @@ -2763,16 +2764,12 @@ subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec) } else if (UNLIKELY(subsec < 0)) { /* subsec negative overflow */ sec2 = NDIV(subsec, maxsubsec); /* negative div */ - if (sec < TIMET_MIN - sec2) { + if (sec < WIDEINT_MIN - sec2) { rb_raise(rb_eRangeError, "out of Time range"); } subsec -= sec2 * maxsubsec; sec += sec2; } -#ifndef NEGATIVE_TIME_T - if (sec < 0) - rb_raise(rb_eArgError, "time must be positive"); -#endif *secp = sec; *subsecp = subsec; } @@ -2780,13 +2777,6 @@ subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec) #define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000) #define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000) -static wideval_t -nsec2timew(time_t sec, long nsec) -{ - time_nsec_normalize(&sec, &nsec); - return timenano2timew(sec, nsec); -} - static VALUE time_new_timew(VALUE klass, wideval_t timew) { @@ -2800,25 +2790,39 @@ time_new_timew(VALUE klass, wideval_t timew) return time; } +static wideint_t +TIMETtoWIDEINT(time_t t) +{ +#if SIZEOF_TIME_T * CHAR_BIT - (SIGNEDNESS_OF_TIME_T < 0) > \ + SIZEOF_WIDEINT * CHAR_BIT - 1 + /* compare in bit size without sign bit */ + if (t > WIDEINT_MAX) rb_raise(rb_eArgError, "out of Time range"); +#endif + return (wideint_t)t; +} + VALUE rb_time_new(time_t sec, long usec) { - time_usec_normalize(&sec, &usec); - return time_new_timew(rb_cTime, timenano2timew(sec, usec * 1000)); + wideint_t isec = TIMETtoWIDEINT(sec); + time_usec_normalize(&isec, &usec); + return time_new_timew(rb_cTime, timenano2timew(isec, usec * 1000)); } /* returns localtime time object */ VALUE rb_time_nano_new(time_t sec, long nsec) { - return time_new_timew(rb_cTime, nsec2timew(sec, nsec)); + wideint_t isec = TIMETtoWIDEINT(sec); + time_nsec_normalize(&isec, &nsec); + return time_new_timew(rb_cTime, timenano2timew(isec, nsec)); } VALUE rb_time_timespec_new(const struct timespec *ts, int offset) { struct time_object *tobj; - VALUE time = time_new_timew(rb_cTime, nsec2timew(ts->tv_sec, ts->tv_nsec)); + VALUE time = rb_time_nano_new(ts->tv_sec, ts->tv_nsec); if (-86400 < offset && offset < 86400) { /* fixoff */ GetTimeval(time, tobj);