Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: xt_time: use time64_t

The current xt_time driver suffers from the y2038 overflow on 32-bit
architectures, when the time of day calculations break.

Also, on both 32-bit and 64-bit architectures, there is a problem with
info->date_start/stop, which is part of the user ABI and overflows in
in 2106.

Fix the first issue by using time64_t and explicit calls to div_u64()
and div_u64_rem(), and document the seconds issue.

The explicit 64-bit division is unfortunately slower on 32-bit
architectures, but doing it as unsigned lets us use the optimized
division-through-multiplication path in most configurations. This should
be fine, as the code already does not allow any negative time of day
values.

Using u32 seconds values consistently would probably also work and
be a little more efficient, but that doesn't feel right as it would
propagate the y2106 overflow to more place rather than fewer.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Arnd Bergmann and committed by
Pablo Neira Ayuso
fcbad829 3944a4fd

+11 -8
+11 -8
net/netfilter/xt_time.c
··· 77 77 * This is done in three separate functions so that the most expensive 78 78 * calculations are done last, in case a "simple match" can be found earlier. 79 79 */ 80 - static inline unsigned int localtime_1(struct xtm *r, time_t time) 80 + static inline unsigned int localtime_1(struct xtm *r, time64_t time) 81 81 { 82 82 unsigned int v, w; 83 83 84 84 /* Each day has 86400s, so finding the hour/minute is actually easy. */ 85 - v = time % SECONDS_PER_DAY; 85 + div_u64_rem(time, SECONDS_PER_DAY, &v); 86 86 r->second = v % 60; 87 87 w = v / 60; 88 88 r->minute = w % 60; ··· 90 90 return v; 91 91 } 92 92 93 - static inline void localtime_2(struct xtm *r, time_t time) 93 + static inline void localtime_2(struct xtm *r, time64_t time) 94 94 { 95 95 /* 96 96 * Here comes the rest (weekday, monthday). First, divide the SSTE 97 97 * by seconds-per-day to get the number of _days_ since the epoch. 98 98 */ 99 - r->dse = time / 86400; 99 + r->dse = div_u64(time, SECONDS_PER_DAY); 100 100 101 101 /* 102 102 * 1970-01-01 (w=0) was a Thursday (4). ··· 105 105 r->weekday = (4 + r->dse - 1) % 7 + 1; 106 106 } 107 107 108 - static void localtime_3(struct xtm *r, time_t time) 108 + static void localtime_3(struct xtm *r, time64_t time) 109 109 { 110 110 unsigned int year, i, w = r->dse; 111 111 ··· 160 160 const struct xt_time_info *info = par->matchinfo; 161 161 unsigned int packet_time; 162 162 struct xtm current_time; 163 - s64 stamp; 163 + time64_t stamp; 164 164 165 165 /* 166 166 * We need real time here, but we can neither use skb->tstamp ··· 173 173 * 1. match before 13:00 174 174 * 2. match after 13:00 175 175 * 176 - * If you match against processing time (get_seconds) it 176 + * If you match against processing time (ktime_get_real_seconds) it 177 177 * may happen that the same packet matches both rules if 178 178 * it arrived at the right moment before 13:00, so it would be 179 179 * better to check skb->tstamp and set it via __net_timestamp() 180 180 * if needed. This however breaks outgoing packets tx timestamp, 181 181 * and causes them to get delayed forever by fq packet scheduler. 182 182 */ 183 - stamp = get_seconds(); 183 + stamp = ktime_get_real_seconds(); 184 184 185 185 if (info->flags & XT_TIME_LOCAL_TZ) 186 186 /* Adjust for local timezone */ ··· 193 193 * - 'now' is in the weekday mask 194 194 * - 'now' is in the daytime range time_start..time_end 195 195 * (and by default, libxt_time will set these so as to match) 196 + * 197 + * note: info->date_start/stop are unsigned 32-bit values that 198 + * can hold values beyond y2038, but not after y2106. 196 199 */ 197 200 198 201 if (stamp < info->date_start || stamp > info->date_stop)