Commit e79e7333 authored by John Stultz's avatar John Stultz Committed by Greg Kroah-Hartman

time: Verify time values in adjtimex ADJ_SETOFFSET to avoid overflow

[ Upstream commit 37cf4dc3 ]

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: default avatarSasha Levin <sasha.levin@oracle.com>
Signed-off-by: default avatarJohn Stultz <john.stultz@linaro.org>
Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent efdd094f
...@@ -125,6 +125,32 @@ static inline bool timeval_valid(const struct timeval *tv) ...@@ -125,6 +125,32 @@ static inline bool timeval_valid(const struct timeval *tv)
extern struct timespec timespec_trunc(struct timespec t, unsigned gran); extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
/*
* Validates if a timespec/timeval used to inject a time offset is valid.
* Offsets can be postive or negative. The value of the timeval/timespec
* is the sum of its fields, but *NOTE*: the field tv_usec/tv_nsec must
* always be non-negative.
*/
static inline bool timeval_inject_offset_valid(const struct timeval *tv)
{
/* We don't check the tv_sec as it can be positive or negative */
/* Can't have more microseconds then a second */
if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC)
return false;
return true;
}
static inline bool timespec_inject_offset_valid(const struct timespec *ts)
{
/* We don't check the tv_sec as it can be positive or negative */
/* Can't have more nanoseconds then a second */
if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
return false;
return true;
}
#define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME (current_kernel_time())
#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
......
...@@ -674,9 +674,15 @@ int ntp_validate_timex(struct timex *txc) ...@@ -674,9 +674,15 @@ int ntp_validate_timex(struct timex *txc)
return -EINVAL; return -EINVAL;
} }
if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) if (txc->modes & ADJ_SETOFFSET) {
/* In order to inject time, you gotta be super-user! */
if (!capable(CAP_SYS_TIME))
return -EPERM; return -EPERM;
if (!timeval_inject_offset_valid(&txc->time))
return -EINVAL;
}
/* /*
* Check for potential multiplication overflows that can * Check for potential multiplication overflows that can
* only happen on 64-bit systems: * only happen on 64-bit systems:
......
...@@ -958,7 +958,7 @@ int timekeeping_inject_offset(struct timespec *ts) ...@@ -958,7 +958,7 @@ int timekeeping_inject_offset(struct timespec *ts)
struct timespec64 ts64, tmp; struct timespec64 ts64, tmp;
int ret = 0; int ret = 0;
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) if (!timespec_inject_offset_valid(ts))
return -EINVAL; return -EINVAL;
ts64 = timespec_to_timespec64(*ts); ts64 = timespec_to_timespec64(*ts);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment