[glib] gdatetime: Fix overflow checks when constructing from timestamps
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] gdatetime: Fix overflow checks when constructing from timestamps
- Date: Fri, 5 May 2017 09:56:02 +0000 (UTC)
commit 9374ecc3cb7f45d47fde150c537402ab9ca9d4af
Author: Philip Withnall <withnall endlessm com>
Date: Tue May 2 23:33:23 2017 +0100
gdatetime: Fix overflow checks when constructing from timestamps
GDateTime does overflow checks to see if the timestamp being passed in
is too big to be represented. However, it only does those after
converting from a timestamp to an interval, which involves some
multiplications and additions — and hence can overflow, and cause the
later bounds check to erroneously succeed. This results in a non-NULL
GDateTime being returned which represents completely the wrong date.
Fix the overflow checks (do them earlier) and add some unit tests.
Signed-off-by: Philip Withnall <withnall endlessm com>
https://bugzilla.gnome.org/show_bug.cgi?id=782089
glib/gdatetime.c | 9 +++++
glib/tests/gdatetime.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 93 insertions(+), 0 deletions(-)
---
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
index 624d74e..7695adf 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -129,6 +129,8 @@ struct _GDateTime
((instant)/USEC_PER_SECOND - UNIX_EPOCH_START * SEC_PER_DAY)
#define UNIX_TO_INSTANT(unix) \
(((unix) + UNIX_EPOCH_START * SEC_PER_DAY) * USEC_PER_SECOND)
+#define UNIX_TO_INSTANT_IS_VALID(unix) \
+ ((gint64) (unix) <= INSTANT_TO_UNIX (G_MAXINT64))
#define DAYS_IN_4YEARS 1461 /* days in 4 years */
#define DAYS_IN_100YEARS 36524 /* days in 100 years */
@@ -650,6 +652,10 @@ static GDateTime *
g_date_time_new_from_timeval (GTimeZone *tz,
const GTimeVal *tv)
{
+ if ((gint64) tv->tv_sec > G_MAXINT64 - 1 ||
+ !UNIX_TO_INSTANT_IS_VALID ((gint64) tv->tv_sec + 1))
+ return NULL;
+
return g_date_time_from_instant (tz, tv->tv_usec +
UNIX_TO_INSTANT (tv->tv_sec));
}
@@ -679,6 +685,9 @@ static GDateTime *
g_date_time_new_from_unix (GTimeZone *tz,
gint64 secs)
{
+ if (!UNIX_TO_INSTANT_IS_VALID (secs))
+ return NULL;
+
return g_date_time_from_instant (tz, UNIX_TO_INSTANT (secs));
}
diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c
index c54031d..db04870 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -139,6 +139,23 @@ test_GDateTime_new_from_unix (void)
g_date_time_unref (dt);
}
+/* Check that trying to create a #GDateTime too far in the future reliably
+ * fails. Previously, the checks for this overflowed and it silently returned
+ * an incorrect #GDateTime. */
+static void
+test_GDateTime_new_from_unix_overflow (void)
+{
+ GDateTime *dt;
+
+ g_test_bug ("782089");
+
+ dt = g_date_time_new_from_unix_utc (G_MAXINT64);
+ g_assert_null (dt);
+
+ dt = g_date_time_new_from_unix_local (G_MAXINT64);
+ g_assert_null (dt);
+}
+
static void
test_GDateTime_invalid (void)
{
@@ -353,6 +370,71 @@ test_GDateTime_new_from_timeval (void)
g_date_time_unref (dt);
}
+static gint64
+find_maximum_supported_tv_sec (void)
+{
+ glong highest_success = 0, lowest_failure = G_MAXLONG;
+ GTimeVal tv;
+
+ tv.tv_usec = 0;
+
+ while (highest_success < lowest_failure - 1)
+ {
+ GDateTime *dt;
+
+ tv.tv_sec = (highest_success + lowest_failure) / 2;
+ dt = g_date_time_new_from_timeval_utc (&tv);
+
+ if (dt != NULL)
+ {
+ highest_success = tv.tv_sec;
+ g_date_time_unref (dt);
+ }
+ else
+ {
+ lowest_failure = tv.tv_sec;
+ }
+ }
+
+ return highest_success;
+}
+
+/* Check that trying to create a #GDateTime too far in the future reliably
+ * fails. With a #GTimeVal, this is subtle, as the tv_usec are added into the
+ * calculation part-way through. */
+static void
+test_GDateTime_new_from_timeval_overflow (void)
+{
+ GDateTime *dt;
+ GTimeVal tv;
+
+ g_test_bug ("782089");
+
+ tv.tv_sec = G_MAXLONG;
+ tv.tv_usec = 0;
+
+ dt = g_date_time_new_from_timeval_utc (&tv);
+ g_assert_null (dt);
+
+ dt = g_date_time_new_from_timeval_local (&tv);
+ g_assert_null (dt);
+
+ tv.tv_sec = find_maximum_supported_tv_sec ();
+ tv.tv_usec = G_USEC_PER_SEC - 1;
+
+ g_test_message ("Maximum supported GTimeVal.tv_sec = %lu", tv.tv_sec);
+
+ dt = g_date_time_new_from_timeval_utc (&tv);
+ g_assert_nonnull (dt);
+ g_date_time_unref (dt);
+
+ tv.tv_sec++;
+ tv.tv_usec = 0;
+
+ dt = g_date_time_new_from_timeval_utc (&tv);
+ g_assert_null (dt);
+}
+
static void
test_GDateTime_new_from_timeval_utc (void)
{
@@ -1652,8 +1734,10 @@ main (gint argc,
g_test_add_func ("/GDateTime/hash", test_GDateTime_hash);
g_test_add_func ("/GDateTime/new_from_unix", test_GDateTime_new_from_unix);
g_test_add_func ("/GDateTime/new_from_unix_utc", test_GDateTime_new_from_unix_utc);
+ g_test_add_func ("/GDateTime/new_from_unix/overflow", test_GDateTime_new_from_unix_overflow);
g_test_add_func ("/GDateTime/new_from_timeval", test_GDateTime_new_from_timeval);
g_test_add_func ("/GDateTime/new_from_timeval_utc", test_GDateTime_new_from_timeval_utc);
+ g_test_add_func ("/GDateTime/new_from_timeval/overflow", test_GDateTime_new_from_timeval_overflow);
g_test_add_func ("/GDateTime/new_full", test_GDateTime_new_full);
g_test_add_func ("/GDateTime/now", test_GDateTime_now);
g_test_add_func ("/GDateTime/printf", test_GDateTime_printf);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]