aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2015-12-03 22:09:31 -0500
committerJohn Stultz <john.stultz@linaro.org>2015-12-11 01:41:06 -0500
commit37cf4dc3370fbca0344e23bb96446eb2c3548ba7 (patch)
treedcefd17bfae395e3f40e4ddf7e9478e3f1bbe94c
parent52d189f1b38810b1b483d5bac2e4fa90b9afd372 (diff)
time: Verify time values in adjtimex ADJ_SETOFFSET to avoid overflow
For adjtimex()'s ADJ_SETOFFSET, make sure the tv_usec value is sane. We might multiply them later which can cause an overflow and undefined behavior. This patch introduces new helper functions to simplify the checking code and adds comments to clarify Orginally this patch was by Sasha Levin, but I've basically rewritten it, so he should get credit for finding the issue and I should get the blame for any mistakes made since. Also, credit to Richard Cochran for the phrasing used in the comment for what is considered valid here. Cc: Sasha Levin <sasha.levin@oracle.com> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Thomas Gleixner <tglx@linutronix.de> Reported-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: John Stultz <john.stultz@linaro.org>
-rw-r--r--include/linux/time.h26
-rw-r--r--kernel/time/ntp.c10
-rw-r--r--kernel/time/timekeeping.c2
3 files changed, 35 insertions, 3 deletions
diff --git a/include/linux/time.h b/include/linux/time.h
index beebe3a02d43..297f09f23896 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -125,6 +125,32 @@ static inline bool timeval_valid(const struct timeval *tv)
125 125
126extern struct timespec timespec_trunc(struct timespec t, unsigned gran); 126extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
127 127
128/*
129 * Validates if a timespec/timeval used to inject a time offset is valid.
130 * Offsets can be postive or negative. The value of the timeval/timespec
131 * is the sum of its fields, but *NOTE*: the field tv_usec/tv_nsec must
132 * always be non-negative.
133 */
134static inline bool timeval_inject_offset_valid(const struct timeval *tv)
135{
136 /* We don't check the tv_sec as it can be positive or negative */
137
138 /* Can't have more microseconds then a second */
139 if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC)
140 return false;
141 return true;
142}
143
144static inline bool timespec_inject_offset_valid(const struct timespec *ts)
145{
146 /* We don't check the tv_sec as it can be positive or negative */
147
148 /* Can't have more nanoseconds then a second */
149 if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
150 return false;
151 return true;
152}
153
128#define CURRENT_TIME (current_kernel_time()) 154#define CURRENT_TIME (current_kernel_time())
129#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) 155#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
130 156
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index 125fc0342355..4073c9550af9 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -676,8 +676,14 @@ int ntp_validate_timex(struct timex *txc)
676 return -EINVAL; 676 return -EINVAL;
677 } 677 }
678 678
679 if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) 679 if (txc->modes & ADJ_SETOFFSET) {
680 return -EPERM; 680 /* In order to inject time, you gotta be super-user! */
681 if (!capable(CAP_SYS_TIME))
682 return -EPERM;
683
684 if (!timeval_inject_offset_valid(&txc->time))
685 return -EINVAL;
686 }
681 687
682 /* 688 /*
683 * Check for potential multiplication overflows that can 689 * Check for potential multiplication overflows that can
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 99188ee5d9d0..d9249daf14ba 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -958,7 +958,7 @@ int timekeeping_inject_offset(struct timespec *ts)
958 struct timespec64 ts64, tmp; 958 struct timespec64 ts64, tmp;
959 int ret = 0; 959 int ret = 0;
960 960
961 if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) 961 if (!timespec_inject_offset_valid(ts))
962 return -EINVAL; 962 return -EINVAL;
963 963
964 ts64 = timespec_to_timespec64(*ts); 964 ts64 = timespec_to_timespec64(*ts);