Skip to content

Commit a84c994

Browse files
committed
Fix crash in Time on 32-bit systems
[Bug #19575] struct vtm is packed causing it to have a size that is not aligned on 32-bit systems. When allocating it on the stack, it will have unaligned addresses which means that the fields won't be marked by the GC when scanning the stack (since the GC only marks aligned addresses). This can cause crashes when the fields are heap allocated objects like Bignums. This commit moves the flags in struct time_object into struct vtm for space efficiency and removes the need for packing. This is an example of a crash: ruby(rb_print_backtrace+0xd) [0x56848945] ../src/vm_dump.c:785 ruby(rb_vm_bugreport) ../src/vm_dump.c:1101 ruby(rb_assert_failure+0x7a) [0x56671857] ../src/error.c:878 ruby(vm_search_cc+0x0) [0x56666e47] ../src/vm_method.c:1366 ruby(rb_vm_search_method_slowpath) ../src/vm_insnhelper.c:2090 ruby(callable_method_entry+0x5) [0x568232d3] ../src/vm_method.c:1406 ruby(rb_callable_method_entry) ../src/vm_method.c:1413 ruby(gccct_method_search_slowpath) ../src/vm_eval.c:427 ruby(gccct_method_search+0x20f) [0x568237ef] ../src/vm_eval.c:476 ruby(opt_equality_by_mid_slowpath+0x2c) [0x5682388c] ../src/vm_insnhelper.c:2338 ruby(rb_equal+0x37) [0x566fe577] ../src/object.c:133 ruby(rb_big_eq+0x34) [0x56876ee4] ../src/bignum.c:5554 ruby(rb_int_equal+0x14) [0x566f3ed4] ../src/numeric.c:4640 ruby(rb_int_equal) ../src/numeric.c:4634 ruby(vm_call0_cfunc_with_frame+0x6d) [0x568303c2] ../src/vm_eval.c:148 ruby(vm_call0_cfunc) ../src/vm_eval.c:162 ruby(vm_call0_body) ../src/vm_eval.c:208 ruby(rb_funcallv_scope+0xd1) [0x56833971] ../src/vm_eval.c:85 ruby(RB_TEST+0x0) [0x567e8488] ../src/time.c:78 ruby(eq) ../src/time.c:78 ruby(small_vtm_sub) ../src/time.c:1523 ruby(timelocalw+0x23b) [0x567f3e9b] ../src/time.c:1593 ruby(time_s_alloc+0x0) [0x567f536b] ../src/time.c:3698 ruby(time_new_timew) ../src/time.c:2694 ruby(time_s_mktime) ../src/time.c:3698
1 parent 06da0d1 commit a84c994

File tree

3 files changed

+34
-37
lines changed

3 files changed

+34
-37
lines changed

test/ruby/test_time.rb

+2-5
Original file line numberDiff line numberDiff line change
@@ -1403,11 +1403,8 @@ def test_memsize
14031403
else
14041404
RbConfig::SIZEOF["void*"] # Same size as VALUE
14051405
end
1406-
expect =
1407-
GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] +
1408-
sizeof_timew +
1409-
RbConfig::SIZEOF["void*"] * 4 + 5 + # vtm
1410-
1 # tzmode, tm_got
1406+
sizeof_vtm = RbConfig::SIZEOF["void*"] * 4 + 8
1407+
expect = GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + sizeof_timew + sizeof_vtm
14111408
assert_equal expect, ObjectSpace.memsize_of(t)
14121409
rescue LoadError => e
14131410
omit "failed to load objspace: #{e.message}"

time.c

+27-30
Original file line numberDiff line numberDiff line change
@@ -1754,42 +1754,39 @@ localtimew(wideval_t timew, struct vtm *result)
17541754
#define TIME_TZMODE_FIXOFF 2
17551755
#define TIME_TZMODE_UNINITIALIZED 3
17561756

1757-
RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN()
17581757
struct time_object {
17591758
wideval_t timew; /* time_t value * TIME_SCALE. possibly Rational. */
17601759
struct vtm vtm;
1761-
unsigned int tzmode:3; /* 0:localtime 1:utc 2:fixoff 3:uninitialized */
1762-
unsigned int tm_got:1;
1763-
} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END();
1760+
};
17641761

17651762
#define GetTimeval(obj, tobj) ((tobj) = get_timeval(obj))
17661763
#define GetNewTimeval(obj, tobj) ((tobj) = get_new_timeval(obj))
17671764

17681765
#define IsTimeval(obj) rb_typeddata_is_kind_of((obj), &time_data_type)
1769-
#define TIME_INIT_P(tobj) ((tobj)->tzmode != TIME_TZMODE_UNINITIALIZED)
1766+
#define TIME_INIT_P(tobj) ((tobj)->vtm.tzmode != TIME_TZMODE_UNINITIALIZED)
17701767

1771-
#define TZMODE_UTC_P(tobj) ((tobj)->tzmode == TIME_TZMODE_UTC)
1772-
#define TZMODE_SET_UTC(tobj) ((tobj)->tzmode = TIME_TZMODE_UTC)
1768+
#define TZMODE_UTC_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_UTC)
1769+
#define TZMODE_SET_UTC(tobj) ((tobj)->vtm.tzmode = TIME_TZMODE_UTC)
17731770

1774-
#define TZMODE_LOCALTIME_P(tobj) ((tobj)->tzmode == TIME_TZMODE_LOCALTIME)
1775-
#define TZMODE_SET_LOCALTIME(tobj) ((tobj)->tzmode = TIME_TZMODE_LOCALTIME)
1771+
#define TZMODE_LOCALTIME_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_LOCALTIME)
1772+
#define TZMODE_SET_LOCALTIME(tobj) ((tobj)->vtm.tzmode = TIME_TZMODE_LOCALTIME)
17761773

1777-
#define TZMODE_FIXOFF_P(tobj) ((tobj)->tzmode == TIME_TZMODE_FIXOFF)
1774+
#define TZMODE_FIXOFF_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_FIXOFF)
17781775
#define TZMODE_SET_FIXOFF(time, tobj, off) do { \
1779-
(tobj)->tzmode = TIME_TZMODE_FIXOFF; \
1776+
(tobj)->vtm.tzmode = TIME_TZMODE_FIXOFF; \
17801777
RB_OBJ_WRITE_UNALIGNED(time, &(tobj)->vtm.utc_offset, off); \
17811778
} while (0)
17821779

17831780
#define TZMODE_COPY(tobj1, tobj2) \
1784-
((tobj1)->tzmode = (tobj2)->tzmode, \
1781+
((tobj1)->vtm.tzmode = (tobj2)->vtm.tzmode, \
17851782
(tobj1)->vtm.utc_offset = (tobj2)->vtm.utc_offset, \
17861783
(tobj1)->vtm.zone = (tobj2)->vtm.zone)
17871784

17881785
static int zone_localtime(VALUE zone, VALUE time);
17891786
static VALUE time_get_tm(VALUE, struct time_object *);
17901787
#define MAKE_TM(time, tobj) \
17911788
do { \
1792-
if ((tobj)->tm_got == 0) { \
1789+
if ((tobj)->vtm.tm_got == 0) { \
17931790
time_get_tm((time), (tobj)); \
17941791
} \
17951792
} while (0)
@@ -1828,7 +1825,7 @@ force_make_tm(VALUE time, struct time_object *tobj)
18281825
if (!NIL_P(zone) && zone != str_empty && zone != str_utc) {
18291826
if (zone_localtime(zone, time)) return;
18301827
}
1831-
tobj->tm_got = 0;
1828+
tobj->vtm.tm_got = 0;
18321829
time_get_tm(time, tobj);
18331830
}
18341831

@@ -1864,8 +1861,8 @@ time_s_alloc(VALUE klass)
18641861
struct time_object *tobj;
18651862

18661863
obj = TypedData_Make_Struct(klass, struct time_object, &time_data_type, tobj);
1867-
tobj->tzmode = TIME_TZMODE_UNINITIALIZED;
1868-
tobj->tm_got = 0;
1864+
tobj->vtm.tzmode = TIME_TZMODE_UNINITIALIZED;
1865+
tobj->vtm.tm_got = 0;
18691866
time_set_timew(obj, tobj, WINT2FIXWV(0));
18701867
tobj->vtm.zone = Qnil;
18711868

@@ -1972,7 +1969,7 @@ time_init_now(rb_execution_context_t *ec, VALUE time, VALUE zone)
19721969
time_modify(time);
19731970
GetNewTimeval(time, tobj);
19741971
TZMODE_SET_LOCALTIME(tobj);
1975-
tobj->tm_got=0;
1972+
tobj->vtm.tm_got=0;
19761973
rb_timespec_now(&ts);
19771974
time_set_timew(time, tobj, timenano2timew(ts.tv_sec, ts.tv_nsec));
19781975

@@ -1998,7 +1995,7 @@ time_set_utc_offset(VALUE time, VALUE off)
19981995
time_modify(time);
19991996
GetTimeval(time, tobj);
20001997

2001-
tobj->tm_got = 0;
1998+
tobj->vtm.tm_got = 0;
20021999
tobj->vtm.zone = Qnil;
20032000
TZMODE_SET_FIXOFF(time, tobj, off);
20042001

@@ -2374,7 +2371,7 @@ zone_localtime(VALUE zone, VALUE time)
23742371
if (UNDEF_P(local)) return 0;
23752372

23762373
s = extract_vtm(local, time, tobj, subsecx);
2377-
tobj->tm_got = 1;
2374+
tobj->vtm.tm_got = 1;
23782375
zone_set_offset(zone, tobj, s, t);
23792376
zone_set_dst(zone, tobj, tm);
23802377
return 1;
@@ -2468,7 +2465,7 @@ time_init_vtm(VALUE time, struct vtm vtm, VALUE zone)
24682465
time_set_timew(time, tobj, timegmw(&vtm));
24692466
vtm_day_wraparound(&vtm);
24702467
time_set_vtm(time, tobj, vtm);
2471-
tobj->tm_got = 1;
2468+
tobj->vtm.tm_got = 1;
24722469
TZMODE_SET_LOCALTIME(tobj);
24732470
if (zone_timelocal(zone, time)) {
24742471
return time;
@@ -2484,13 +2481,13 @@ time_init_vtm(VALUE time, struct vtm vtm, VALUE zone)
24842481
vtm.isdst = 0; /* No DST in UTC */
24852482
vtm_day_wraparound(&vtm);
24862483
time_set_vtm(time, tobj, vtm);
2487-
tobj->tm_got = 1;
2484+
tobj->vtm.tm_got = 1;
24882485
TZMODE_SET_UTC(tobj);
24892486
return time;
24902487
}
24912488

24922489
TZMODE_SET_LOCALTIME(tobj);
2493-
tobj->tm_got=0;
2490+
tobj->vtm.tm_got=0;
24942491

24952492
if (!NIL_P(vtm.utc_offset)) {
24962493
VALUE off = vtm.utc_offset;
@@ -3996,7 +3993,7 @@ time_localtime(VALUE time)
39963993

39973994
GetTimeval(time, tobj);
39983995
if (TZMODE_LOCALTIME_P(tobj)) {
3999-
if (tobj->tm_got)
3996+
if (tobj->vtm.tm_got)
40003997
return time;
40013998
}
40023999
else {
@@ -4012,7 +4009,7 @@ time_localtime(VALUE time)
40124009
rb_raise(rb_eArgError, "localtime error");
40134010
time_set_vtm(time, tobj, vtm);
40144011

4015-
tobj->tm_got = 1;
4012+
tobj->vtm.tm_got = 1;
40164013
TZMODE_SET_LOCALTIME(tobj);
40174014
return time;
40184015
}
@@ -4097,7 +4094,7 @@ time_gmtime(VALUE time)
40974094

40984095
GetTimeval(time, tobj);
40994096
if (TZMODE_UTC_P(tobj)) {
4100-
if (tobj->tm_got)
4097+
if (tobj->vtm.tm_got)
41014098
return time;
41024099
}
41034100
else {
@@ -4108,7 +4105,7 @@ time_gmtime(VALUE time)
41084105
GMTIMEW(tobj->timew, &vtm);
41094106
time_set_vtm(time, tobj, vtm);
41104107

4111-
tobj->tm_got = 1;
4108+
tobj->vtm.tm_got = 1;
41124109
TZMODE_SET_UTC(tobj);
41134110
return time;
41144111
}
@@ -4122,7 +4119,7 @@ time_fixoff(VALUE time)
41224119

41234120
GetTimeval(time, tobj);
41244121
if (TZMODE_FIXOFF_P(tobj)) {
4125-
if (tobj->tm_got)
4122+
if (tobj->vtm.tm_got)
41264123
return time;
41274124
}
41284125
else {
@@ -4142,7 +4139,7 @@ time_fixoff(VALUE time)
41424139
time_set_vtm(time, tobj, vtm);
41434140
RB_OBJ_WRITE_UNALIGNED(time, &tobj->vtm.zone, zone);
41444141

4145-
tobj->tm_got = 1;
4142+
tobj->vtm.tm_got = 1;
41464143
TZMODE_SET_FIXOFF(time, tobj, off);
41474144
return time;
41484145
}
@@ -5496,7 +5493,7 @@ end_submicro: ;
54965493

54975494
GetNewTimeval(time, tobj);
54985495
TZMODE_SET_LOCALTIME(tobj);
5499-
tobj->tm_got = 0;
5496+
tobj->vtm.tm_got = 0;
55005497
time_set_timew(time, tobj, timew);
55015498

55025499
if (gmt) {
@@ -5561,7 +5558,7 @@ tm_from_time(VALUE klass, VALUE time)
55615558
v->zone = Qnil;
55625559
time_set_vtm(tm, ttm, *v);
55635560

5564-
ttm->tm_got = 1;
5561+
ttm->vtm.tm_got = 1;
55655562
TZMODE_SET_UTC(ttm);
55665563
return tm;
55675564
}

timev.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#define RUBY_TIMEV_H
33
#include "ruby/ruby.h"
44

5-
RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN()
65
struct vtm {
76
VALUE year; /* 2000 for example. Integer. */
87
VALUE subsecx; /* 0 <= subsecx < TIME_SCALE. possibly Rational. */
@@ -16,7 +15,11 @@ struct vtm {
1615
unsigned int sec:6; /* 0..60 */
1716
unsigned int wday:3; /* 0:Sunday, 1:Monday, ..., 6:Saturday 7:init */
1817
unsigned int isdst:2; /* 0:StandardTime 1:DayLightSavingTime 3:init */
19-
} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END();
18+
19+
/* Flags for struct time_object */
20+
unsigned int tzmode:3; /* 0:localtime 1:utc 2:fixoff 3:uninitialized */
21+
unsigned int tm_got:1;
22+
};
2023

2124
#define TIME_SCALE 1000000000
2225

0 commit comments

Comments
 (0)