From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.org/gmane.linux.lib.musl.general/8520 Path: news.gmane.org!not-for-mail From: Szabolcs Nagy Newsgroups: gmane.linux.lib.musl.general Subject: out of range struct tm fields in strftime Date: Sun, 20 Sep 2015 14:44:50 +0200 Message-ID: <20150920124450.GA10551@port70.net> Reply-To: musl@lists.openwall.com NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="W/nzBZO5zC0uMSeA" X-Trace: ger.gmane.org 1442753120 798 80.91.229.3 (20 Sep 2015 12:45:20 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sun, 20 Sep 2015 12:45:20 +0000 (UTC) To: musl@lists.openwall.com Original-X-From: musl-return-8532-gllmg-musl=m.gmane.org@lists.openwall.com Sun Sep 20 14:45:15 2015 Return-path: Envelope-to: gllmg-musl@m.gmane.org Original-Received: from mother.openwall.net ([195.42.179.200]) by plane.gmane.org with smtp (Exim 4.69) (envelope-from ) id 1ZddzW-0007sW-GS for gllmg-musl@m.gmane.org; Sun, 20 Sep 2015 14:45:14 +0200 Original-Received: (qmail 28408 invoked by uid 550); 20 Sep 2015 12:45:10 -0000 Mailing-List: contact musl-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: Original-Received: (qmail 28320 invoked from network); 20 Sep 2015 12:45:02 -0000 Mail-Followup-To: musl@lists.openwall.com Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) Xref: news.gmane.org gmane.linux.lib.musl.general:8520 Archived-At: --W/nzBZO5zC0uMSeA Content-Type: text/plain; charset=us-ascii Content-Disposition: inline out of range tm fields should not be treated as ub as noted in the thread http://sourceware.org/ml/libc-alpha/2015-09/msg00546.html i have a patch but there might be simpler approaches --W/nzBZO5zC0uMSeA Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0001-fix-strftime-to-handle-out-of-range-tm-fields-withou.patch" >From 042305a745991d2de8e7fd0111f158086d283f35 Mon Sep 17 00:00:00 2001 From: Szabolcs Nagy Date: Sun, 20 Sep 2015 12:02:21 +0000 Subject: [PATCH] fix strftime to handle out of range tm fields without UB Calling strftime with out of range tm fields is not undefined behaviour, it should return a result (though in this case the stored string is unspecified). tm_wday, tm_yday, tm_mon and tm_year fields are used in signed int arithmetics that may overflow. For tm_year long long arithmetics is used and the rest is fixed by limiting the input. --- src/time/strftime.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/time/strftime.c b/src/time/strftime.c index e945bb7..464b546 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -19,27 +19,27 @@ static int is_leap(int y) return !(y%4) && ((y%100) || !(y%400)); } -static int week_num(const struct tm *tm) +static int week_num(int wday, int yday, int year) { - int val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7; + int val = (yday + 7 - (wday+6)%7) / 7; /* If 1 Jan is just 1-3 days past Monday, * the previous week is also in this year. */ - if ((tm->tm_wday - tm->tm_yday - 2 + 371) % 7 <= 2) + if ((wday - yday - 2 + 371) % 7 <= 2) val++; if (!val) { val = 52; /* If 31 December of prev year a Thursday, * or Friday of a leap year, then the * prev year has 53 weeks. */ - int dec31 = (tm->tm_wday - tm->tm_yday - 1 + 7) % 7; - if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1))) + int dec31 = (wday - yday - 1 + 7) % 7; + if (dec31 == 4 || (dec31 == 5 && is_leap(year%400-1))) val++; } else if (val == 53) { /* If 1 January is not a Thursday, and not * a Wednesday of a leap year, then this * year has only 52 weeks. */ - int jan1 = (tm->tm_wday - tm->tm_yday + 371) % 7; - if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year))) + int jan1 = (wday - yday + 371) % 7; + if (jan1 != 4 && (jan1 != 3 || !is_leap(year))) val = 1; } return val; @@ -55,19 +55,23 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * const char *fmt; int width = 2; + int wday = tm->tm_wday < 7U ? tm->tm_wday : 0; + int yday = tm->tm_yday < 366U ? tm->tm_yday : 0; + int mon = tm->tm_mon < 12U ? tm->tm_mon : 0; + switch (f) { case 'a': - item = ABDAY_1 + tm->tm_wday; + item = ABDAY_1 + wday; goto nl_strcat; case 'A': - item = DAY_1 + tm->tm_wday; + item = DAY_1 + wday; goto nl_strcat; case 'h': case 'b': - item = ABMON_1 + tm->tm_mon; + item = ABMON_1 + mon; goto nl_strcat; case 'B': - item = MON_1 + tm->tm_mon; + item = MON_1 + mon; goto nl_strcat; case 'c': item = D_T_FMT; @@ -90,8 +94,8 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * case 'g': case 'G': val = tm->tm_year + 1900LL; - if (tm->tm_yday < 3 && week_num(tm) != 1) val--; - else if (tm->tm_yday > 360 && week_num(tm) == 1) val++; + if (yday < 3 && week_num(wday, yday, tm->tm_year) != 1) val--; + else if (yday > 360 && week_num(wday, yday, tm->tm_year) == 1) val++; if (f=='g') val %= 100; else width = 4; goto number; @@ -104,11 +108,11 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * else if (val > 12) val -= 12; goto number; case 'j': - val = tm->tm_yday+1; + val = yday+1; width = 3; goto number; case 'm': - val = tm->tm_mon+1; + val = mon+1; goto number; case 'M': val = tm->tm_min; @@ -139,20 +143,20 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * fmt = "%H:%M:%S"; goto recu_strftime; case 'u': - val = tm->tm_wday ? tm->tm_wday : 7; + val = wday ? wday : 7; width = 1; goto number; case 'U': - val = (tm->tm_yday + 7 - tm->tm_wday) / 7; + val = (yday + 7 - wday) / 7; goto number; case 'W': - val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7; + val = (yday + 7 - (wday+6)%7) / 7; goto number; case 'V': - val = week_num(tm); + val = week_num(wday, yday, tm->tm_year); goto number; case 'w': - val = tm->tm_wday; + val = wday; width = 1; goto number; case 'x': @@ -165,7 +169,7 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * val = tm->tm_year % 100; goto number; case 'Y': - val = tm->tm_year + 1900; + val = tm->tm_year + 1900LL; if (val >= 10000) { *l = snprintf(*s, sizeof *s, "+%lld", val); return *s; -- 2.4.1 --W/nzBZO5zC0uMSeA--