From patchwork Fri Dec 18 21:39:12 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Stultz X-Patchwork-Id: 58760 Delivered-To: patches@linaro.org Received: by 10.112.89.199 with SMTP id bq7csp1285519lbb; Fri, 18 Dec 2015 13:39:44 -0800 (PST) X-Received: by 10.98.71.217 with SMTP id p86mr8596386pfi.132.1450474770181; Fri, 18 Dec 2015 13:39:30 -0800 (PST) Return-Path: Received: from mail-pa0-x22b.google.com (mail-pa0-x22b.google.com. [2607:f8b0:400e:c03::22b]) by mx.google.com with ESMTPS id d22si17505576pfj.41.2015.12.18.13.39.29 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 18 Dec 2015 13:39:30 -0800 (PST) Received-SPF: pass (google.com: domain of john.stultz@linaro.org designates 2607:f8b0:400e:c03::22b as permitted sender) client-ip=2607:f8b0:400e:c03::22b; Authentication-Results: mx.google.com; spf=pass (google.com: domain of john.stultz@linaro.org designates 2607:f8b0:400e:c03::22b as permitted sender) smtp.mailfrom=john.stultz@linaro.org; dkim=pass header.i=@linaro.org Received: by mail-pa0-x22b.google.com with SMTP id jx14so37454200pad.2 for ; Fri, 18 Dec 2015 13:39:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Cz8CSVhgAWXOm3rldRFLZnvWCyt7cy/AoIDz4pOQpGE=; b=PbvobbnAnLXEMxjoT0IUwqzBRsJ9zne/p1wKeQPVswE00nv7qwLW/pT3fRbXnp2bcj JdB0+6TpbGQQDWK0/ZnysysjmgRLLL1267cdiNeZC3tho+Hu5wmag0ZSNawONjRBjR54 sbC7I+PWOwGV+LitaNYYKMsJ5RDDTTy/bEET4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Cz8CSVhgAWXOm3rldRFLZnvWCyt7cy/AoIDz4pOQpGE=; b=TJ58Zuua2nOWwv74FyyjO3DE9pMQSEP9rupKXDTm+Mw7IUGQSRr88yM985SX1Jwjyi Hxh0X6zOWns4fd8lPYK3n9FOQ+azy+v1c+2zswTLeEJggL9Ocri2zn8o6HfZzOOPnNkU ssPySygHuq20vOP5ovEbdSDgBFQqejThP1jQXMxmsWvpTV56wCrj0CqDw6o+yY8Ih2c9 PGJS1Mm6UkFWAWzW7I48OnoL6VTCE1GYkWS1tBKWzYpM0UrK4aTPUw30MuYXR/3SRYV3 wQUTWhNEd3jAI8+AHA+vPs6fx4wSINuVR02INCIfTu6Vta9zAV5W50ddHu0b8fEO4aIw bsFA== X-Gm-Message-State: ALoCoQluQ2ELYrUnLLen2GEXNu0KrghK/z0kaQ+kcJ4sEb7C5xLpvFBugSZodYbWLOX/7Dek3cY4vCaa1tMDn083bN4mgpgz6A== X-Received: by 10.67.5.98 with SMTP id cl2mr8523218pad.157.1450474769816; Fri, 18 Dec 2015 13:39:29 -0800 (PST) Return-Path: Received: from localhost.localdomain (c-76-115-103-22.hsd1.or.comcast.net. [76.115.103.22]) by smtp.gmail.com with ESMTPSA id w62sm19642284pfi.48.2015.12.18.13.39.29 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 18 Dec 2015 13:39:29 -0800 (PST) From: John Stultz To: lkml Cc: John Stultz , Sasha Levin , Thomas Gleixner , Ingo Molnar , Miroslav Lichvar , Prarit Bhargava , Richard Cochran Subject: [PATCH 07/11] time: Verify time values in adjtimex ADJ_SETOFFSET to avoid overflow Date: Fri, 18 Dec 2015 13:39:12 -0800 Message-Id: <1450474756-10144-8-git-send-email-john.stultz@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1450474756-10144-1-git-send-email-john.stultz@linaro.org> References: <1450474756-10144-1-git-send-email-john.stultz@linaro.org> 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 Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Miroslav Lichvar Cc: Prarit Bhargava Cc: Richard Cochran Reported-by: Sasha Levin Signed-off-by: John Stultz --- include/linux/time.h | 26 ++++++++++++++++++++++++++ kernel/time/ntp.c | 10 ++++++++-- kernel/time/timekeeping.c | 2 +- 3 files changed, 35 insertions(+), 3 deletions(-) -- 1.9.1 diff --git a/include/linux/time.h b/include/linux/time.h index beebe3a..297f09f 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -125,6 +125,32 @@ static inline bool timeval_valid(const struct timeval *tv) 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_SEC ((struct timespec) { get_seconds(), 0 }) diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 125fc03..4073c95 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -676,8 +676,14 @@ int ntp_validate_timex(struct timex *txc) return -EINVAL; } - if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) - return -EPERM; + if (txc->modes & ADJ_SETOFFSET) { + /* In order to inject time, you gotta be super-user! */ + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (!timeval_inject_offset_valid(&txc->time)) + return -EINVAL; + } /* * Check for potential multiplication overflows that can diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 99188ee..d9249da 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -958,7 +958,7 @@ int timekeeping_inject_offset(struct timespec *ts) struct timespec64 ts64, tmp; int ret = 0; - if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) + if (!timespec_inject_offset_valid(ts)) return -EINVAL; ts64 = timespec_to_timespec64(*ts);