Patchwork [1/3] rtc: fix overflow in mktimegm

login
register
mail settings
Submitter Paolo Bonzini
Date Oct. 1, 2012, 12:22 p.m.
Message ID <1349094128-32332-2-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/188281/
State New
Headers show

Comments

Paolo Bonzini - Oct. 1, 2012, 12:22 p.m.
When setting a date in 1980, Linux is actually disregarding the century
byte and setting the year to 2080.  This causes a year-2038 overflow
in mktimegm.  Fix this by doing the days-to-seconds computation in
64-bit math.

Reported-by: Lucas Meneghel Rodrigues <lookkas@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 cutils.c         |  2 +-
 tests/rtc-test.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 file modificati, 46 inserzioni(+). 1 rimozione(-)
Anthony Liguori - Oct. 5, 2012, 9:19 p.m.
Paolo Bonzini <pbonzini@redhat.com> writes:

> When setting a date in 1980, Linux is actually disregarding the century
> byte and setting the year to 2080.  This causes a year-2038 overflow
> in mktimegm.  Fix this by doing the days-to-seconds computation in
> 64-bit math.
>
> Reported-by: Lucas Meneghel Rodrigues <lookkas@gmail.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Applied. Thanks.

Regards,

Anthony Liguori

> ---
>  cutils.c         |  2 +-
>  tests/rtc-test.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  2 file modificati, 46 inserzioni(+). 1 rimozione(-)
>
> diff --git a/cutils.c b/cutils.c
> index 8ef648f..8edd8fa 100644
> --- a/cutils.c
> +++ b/cutils.c
> @@ -115,7 +115,7 @@ time_t mktimegm(struct tm *tm)
>          m += 12;
>          y--;
>      }
> -    t = 86400 * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + 
> +    t = 86400ULL * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + 
>                   y / 400 - 719469);
>      t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
>      return t;
> diff --git a/tests/rtc-test.c b/tests/rtc-test.c
> index f23ac3a..2b9aa63 100644
> --- a/tests/rtc-test.c
> +++ b/tests/rtc-test.c
> @@ -179,6 +179,50 @@ static void check_time(int wiggle)
>  
>  static int wiggle = 2;
>  
> +static void set_year(void)
> +{
> +    /* Set BCD mode */
> +    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM);
> +    cmos_write(RTC_REG_A, 0x76);
> +    cmos_write(RTC_YEAR, 0x11);
> +    cmos_write(RTC_MONTH, 0x02);
> +    cmos_write(RTC_DAY_OF_MONTH, 0x02);
> +    cmos_write(RTC_HOURS, 0x02);
> +    cmos_write(RTC_MINUTES, 0x04);
> +    cmos_write(RTC_SECONDS, 0x58);
> +    cmos_write(RTC_REG_A, 0x26);
> +
> +    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
> +    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
> +    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
> +
> +    /* Set a date in 2080 to ensure there is no year-2038 overflow.  */
> +    cmos_write(RTC_REG_A, 0x76);
> +    cmos_write(RTC_YEAR, 0x80);
> +    cmos_write(RTC_REG_A, 0x26);
> +
> +    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
> +    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
> +    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80);
> +
> +    cmos_write(RTC_REG_A, 0x76);
> +    cmos_write(RTC_YEAR, 0x11);
> +    cmos_write(RTC_REG_A, 0x26);
> +
> +    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
> +    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
> +    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
> +    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
> +}
> +
>  static void bcd_check_time(void)
>  {
>      /* Set BCD mode */
> @@ -269,6 +313,7 @@ int main(int argc, char **argv)
>      qtest_add_func("/rtc/bcd/check-time", bcd_check_time);
>      qtest_add_func("/rtc/dec/check-time", dec_check_time);
>      qtest_add_func("/rtc/alarm-time", alarm_time);
> +    qtest_add_func("/rtc/set-year", set_year);
>      qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
>      ret = g_test_run();
>  
> -- 
> 1.7.12

Patch

diff --git a/cutils.c b/cutils.c
index 8ef648f..8edd8fa 100644
--- a/cutils.c
+++ b/cutils.c
@@ -115,7 +115,7 @@  time_t mktimegm(struct tm *tm)
         m += 12;
         y--;
     }
-    t = 86400 * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + 
+    t = 86400ULL * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + 
                  y / 400 - 719469);
     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
     return t;
diff --git a/tests/rtc-test.c b/tests/rtc-test.c
index f23ac3a..2b9aa63 100644
--- a/tests/rtc-test.c
+++ b/tests/rtc-test.c
@@ -179,6 +179,50 @@  static void check_time(int wiggle)
 
 static int wiggle = 2;
 
+static void set_year(void)
+{
+    /* Set BCD mode */
+    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM);
+    cmos_write(RTC_REG_A, 0x76);
+    cmos_write(RTC_YEAR, 0x11);
+    cmos_write(RTC_MONTH, 0x02);
+    cmos_write(RTC_DAY_OF_MONTH, 0x02);
+    cmos_write(RTC_HOURS, 0x02);
+    cmos_write(RTC_MINUTES, 0x04);
+    cmos_write(RTC_SECONDS, 0x58);
+    cmos_write(RTC_REG_A, 0x26);
+
+    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
+    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
+    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
+
+    /* Set a date in 2080 to ensure there is no year-2038 overflow.  */
+    cmos_write(RTC_REG_A, 0x76);
+    cmos_write(RTC_YEAR, 0x80);
+    cmos_write(RTC_REG_A, 0x26);
+
+    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
+    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
+    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80);
+
+    cmos_write(RTC_REG_A, 0x76);
+    cmos_write(RTC_YEAR, 0x11);
+    cmos_write(RTC_REG_A, 0x26);
+
+    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
+    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
+    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
+}
+
 static void bcd_check_time(void)
 {
     /* Set BCD mode */
@@ -269,6 +313,7 @@  int main(int argc, char **argv)
     qtest_add_func("/rtc/bcd/check-time", bcd_check_time);
     qtest_add_func("/rtc/dec/check-time", dec_check_time);
     qtest_add_func("/rtc/alarm-time", alarm_time);
+    qtest_add_func("/rtc/set-year", set_year);
     qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
     ret = g_test_run();