Once we get a glibc with 64-bit time_t, the LPSETTIMEOUT ioctl stops
working, since the command number and data structure no longer match.
To work around that, this introduces a new command number LPSETTIMEOUT_NEW
that is used whenever the modified user space evaluates the LPSETTIMEOUT
macro.
The trick we use is a bit convoluted but necessary: we cannot check for
any macros set by the C library in linux/lp.h, because this particular
header can be included before including sys/time.h. However, we can assume
that by the time that LPSETTIMEOUT is seen in the code, the definition
for 'timeval' and 'time_t' has been seen as well, so we can use the
sizeof() operator to determine whether we should use the old or the
new definition. We use the old one not only for traditional 32-bit user
space with 32-bit time_t, but also for all 64-bit architectures and x32,
which always use a 64-bit time_t, the new definition will be used only for
32-bit user space with 64-bit time_t, which also requires a newer kernel.
The compat_ioctl() handler now implements both commands, but has to
use a special case for existing x32 binaries. The native ioctl handler
now implements both command numbers on both 32-bit and 64-bit, though
the latter version use the same interpretation for both.
This is based on an earlier patch from Bamvor.
Cc: Bamvor Jian Zhang <bamv2005(a)gmail.com>
Link: http://www.spinics.net/lists/y2038/msg01162.html
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
drivers/char/lp.c | 67 +++++++++++++++++++++++++++++++++++++------------
include/uapi/linux/lp.h | 12 ++++++++-
2 files changed, 62 insertions(+), 17 deletions(-)
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 8249762192d5..be14abf70da1 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -659,17 +659,31 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd,
return retval;
}
-static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout)
+static int lp_set_timeout(unsigned int minor, s64 tv_sec, long tv_usec)
{
long to_jiffies;
/* Convert to jiffies, place in lp_table */
- if ((par_timeout->tv_sec < 0) ||
- (par_timeout->tv_usec < 0)) {
+ if (tv_sec < 0 || tv_usec < 0)
return -EINVAL;
+
+ /*
+ * we used to not check, so let's not make this fatal,
+ * but deal with user space passing a 32-bit tv_nsec in
+ * a 64-bit field, capping the timeout to 1 second
+ * worth of microseconds, and capping the total at
+ * MAX_JIFFY_OFFSET.
+ */
+ if (tv_usec > 999999)
+ tv_usec = 999999;
+
+ if (tv_sec >= MAX_SEC_IN_JIFFIES - 1) {
+ to_jiffies = MAX_JIFFY_OFFSET;
+ } else {
+ to_jiffies = DIV_ROUND_UP(tv_usec, 1000000/HZ);
+ to_jiffies += tv_sec * (long) HZ;
}
- to_jiffies = DIV_ROUND_UP(par_timeout->tv_usec, 1000000/HZ);
- to_jiffies += par_timeout->tv_sec * (long) HZ;
+
if (to_jiffies <= 0) {
return -EINVAL;
}
@@ -677,23 +691,43 @@ static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout)
return 0;
}
+static int lp_set_timeout32(unsigned int minor, void __user *arg)
+{
+ s32 karg[2];
+
+ if (copy_from_user(karg, arg, sizeof(karg)))
+ return -EFAULT;
+
+ return lp_set_timeout(minor, karg[0], karg[1]);
+}
+
+static int lp_set_timeout64(unsigned int minor, void __user *arg)
+{
+ s64 karg[2];
+
+ if (copy_from_user(karg, arg, sizeof(karg)))
+ return -EFAULT;
+
+ return lp_set_timeout(minor, karg[0], karg[1]);
+}
+
static long lp_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned int minor;
- struct timeval par_timeout;
int ret;
minor = iminor(file_inode(file));
mutex_lock(&lp_mutex);
switch (cmd) {
- case LPSETTIMEOUT:
- if (copy_from_user(&par_timeout, (void __user *)arg,
- sizeof (struct timeval))) {
- ret = -EFAULT;
+ case LPSETTIMEOUT_OLD:
+ if (BITS_PER_LONG == 32) {
+ ret = lp_set_timeout32(minor, (void __user *)arg);
break;
}
- ret = lp_set_timeout(minor, &par_timeout);
+ /* fallthrough for 64-bit */
+ case LPSETTIMEOUT_NEW:
+ ret = lp_set_timeout64(minor, (void __user *)arg);
break;
default:
ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg);
@@ -709,18 +743,19 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned int minor;
- struct timeval par_timeout;
int ret;
minor = iminor(file_inode(file));
mutex_lock(&lp_mutex);
switch (cmd) {
- case LPSETTIMEOUT:
- if (compat_get_timeval(&par_timeout, compat_ptr(arg))) {
- ret = -EFAULT;
+ case LPSETTIMEOUT_OLD:
+ if (!COMPAT_USE_64BIT_TIME) {
+ ret = lp_set_timeout32(minor, (void __user *)arg);
break;
}
- ret = lp_set_timeout(minor, &par_timeout);
+ /* fallthrough for x32 mode */
+ case LPSETTIMEOUT_NEW:
+ ret = lp_set_timeout64(minor, (void __user *)arg);
break;
#ifdef LP_STATS
case LPGETSTATS:
diff --git a/include/uapi/linux/lp.h b/include/uapi/linux/lp.h
index dafcfe4e4834..8589a27037d7 100644
--- a/include/uapi/linux/lp.h
+++ b/include/uapi/linux/lp.h
@@ -8,6 +8,8 @@
#ifndef _UAPI_LINUX_LP_H
#define _UAPI_LINUX_LP_H
+#include <linux/types.h>
+#include <linux/ioctl.h>
/*
* Per POSIX guidelines, this module reserves the LP and lp prefixes
@@ -88,7 +90,15 @@
#define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */
#endif
#define LPGETFLAGS 0x060e /* get status flags */
-#define LPSETTIMEOUT 0x060f /* set parport timeout */
+#define LPSETTIMEOUT_OLD 0x060f /* set parport timeout */
+#define LPSETTIMEOUT_NEW \
+ _IOW(0x6, 0xf, __s64[2]) /* set parport timeout */
+#if __BITS_PER_LONG == 64
+#define LPSETTIMEOUT LPSETTIMEOUT_OLD
+#else
+#define LPSETTIMEOUT (sizeof(time_t) > sizeof(__kernel_long_t) ? \
+ LPSETTIMEOUT_NEW : LPSETTIMEOUT_OLD)
+#endif
/* timeout for printk'ing a timeout, in jiffies (100ths of a second).
This is also used for re-checking error conditions if LP_ABORT is
--
2.9.0
The bfa driver is one of the main users of do_gettimeofday(), a function
that I'm trying to remove as part of the y2038 cleanup.
The timestamps are all uses in slightly different ways, so this has turned
into a rather longish series for doing something that should be simple.
The last patch in the series ("scsi: bfa: use 64-bit times in
bfa_aen_entry_s ABI") is one that needs to be reviewed very carefully,
and it can be skipped if the maintainers prefer to leave the 32-bit ABI
unchanged, the rest are hopefully fairly straightforward.
Arnd
Arnd Bergmann (7):
scsi: bfa: use ktime_get_real_ts64 for firmware timestamp
scsi: bfa: use proper time accessor for stats_reset_time
scsi: bfa: improve bfa_ioc_send_enable/disable data
scsi: bfa: document overflow of io_profile_start_time
scsi: bfa: replace bfa_get_log_time() with ktime_get_real_seconds()
scsi: bfa: try to sanitize vendor netlink events
scsi: bfa: use 64-bit times in bfa_aen_entry_s ABI
drivers/scsi/bfa/bfa_cs.h | 6 +++---
drivers/scsi/bfa/bfa_defs_svc.h | 3 ++-
drivers/scsi/bfa/bfa_fcpim.c | 3 ++-
drivers/scsi/bfa/bfa_fcpim.h | 4 ++--
drivers/scsi/bfa/bfa_ioc.c | 8 ++++---
drivers/scsi/bfa/bfa_port.c | 15 +++----------
drivers/scsi/bfa/bfa_port.h | 2 +-
drivers/scsi/bfa/bfa_svc.c | 47 ++++++++++++-----------------------------
drivers/scsi/bfa/bfa_svc.h | 2 +-
drivers/scsi/bfa/bfad_bsg.c | 4 +---
drivers/scsi/bfa/bfad_im.h | 32 +++++++++++++++++++---------
11 files changed, 56 insertions(+), 70 deletions(-)
--
2.9.0
There was a typo in the new version of put_tv32() that caused an unguarded
access of a user space pointer, and failed to return the correct result in
gettimeofday(), wait4(), usleep_thread() and old_adjtimex().
This fixes it to give the correct behavior again.
Cc: stable(a)vger.kernel.org
Fixes: 1cc6c4635e9f ("osf_sys.c: switch handling of timeval32/itimerval32 to copy_{to,from}_user()")
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
v2: fix incorrect changelog description
---
arch/alpha/kernel/osf_sys.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index ce3a675c0c4b..75a5c35a2067 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -964,8 +964,8 @@ static inline long
put_tv32(struct timeval32 __user *o, struct timeval *i)
{
return copy_to_user(o, &(struct timeval32){
- .tv_sec = o->tv_sec,
- .tv_usec = o->tv_usec},
+ .tv_sec = i->tv_sec,
+ .tv_usec = i->tv_usec},
sizeof(struct timeval32));
}
--
2.9.0
There was a typo in the new version of put_tv32() that caused
uninitialized stack data to be written back to user space, rather
than writing the actual timeval for the emulation of
gettimeofday(), wait4(), usleep_thread() and old_adjtimex().
This fixes it to write the correct data again.
Cc: stable(a)vger.kernel.org
Fixes: 1cc6c4635e9f ("osf_sys.c: switch handling of timeval32/itimerval32 to copy_{to,from}_user()")
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
arch/alpha/kernel/osf_sys.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index ce3a675c0c4b..75a5c35a2067 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -964,8 +964,8 @@ static inline long
put_tv32(struct timeval32 __user *o, struct timeval *i)
{
return copy_to_user(o, &(struct timeval32){
- .tv_sec = o->tv_sec,
- .tv_usec = o->tv_usec},
+ .tv_sec = i->tv_sec,
+ .tv_usec = i->tv_usec},
sizeof(struct timeval32));
}
--
2.9.0
There was a meeting at ELCE about the status and development of Y2038-
safe APIs in Linux and GNU libc. This included several developers from
members of CIP, plus Arnd Bergmann, Mark Brown and Albert Aribaud.
Below are my notes from the meeting.
Ben.
## Kernel
Arnd Bergmann started working on Y2038 about 6 years ago, initially
looking at file-systems and VFS. File-systems are mostly ready but VFS
hasn't been switched over yet.
The largest missing piece is the syscall interface. "We know what we
want to do." There was a complete patch set for an older version, but
it has not been completely rebased onto current mainline. On 32-bit
systems about 35 syscalls use 32-bit time, but half of those are
already obsolete. Something like 22 new syscalls will be needed.
Network, sound, media, key management, input have not been dealt with
yet. Patches are available for some of these but they can be invasive
and hard to review. This is a low priority for some subsystem
maintainers.
About 100 device drivers need changes, ranging from an obvious 1 line
change to a week's work.
About 10% of the changes are needed for Y2038 safety on both 32-bit
and 64-bit architectures, the rest only for 32-bit.
Arnd wants to include a kconfig option to disable the 32-bit time
APIs, so that any remaining users are easy to detect.
## GNU libc
Albert Aribaut talked about the status of glibc. It will need to
support both 32-bit `time_t` and 64-bit `time_t` independently of the
kernel. A draft specification for this exists at
<https://sourceware.org/glibc/wiki/Y2038ProofnessDesign>. About 60
APIs are affected, using `time_t` or derived type.
Ideally source can be rebuilt to use 64-bit `time_t` just by
defining the feature macro to enable it.
The implementation is not complete, partly because syscalls haven't
yet been defined.
## Other C libraries
Arnd says some other C libraries will support 64-bit `time_t` but as a
build-time option. I.e. libc and all applications must be built for
either 32-bit or 64-bit `time_t`.
## Application compatibility issues
If Unix timestamps are used in binary file formats or network
protocols, these will need a new version. In some cases switching to
unsigned 32-bit values is easy and will work for long enough.
If `time_t` is used in library APIs then an ABI change is required.
cppcheck(?) can find instances of this.
Some libraries may use their own time types, so changing `time_t`
won't be an ABI change but they will need to be updated anyway.
Printing a value of type `time_t` with `printf()` and similar
functions requires casting as there's no format specifier for it. It
will be necessary to cast to `long long`, whereas previously `long`
would work.
The sparse static checker is supposed to be able to check for
truncating conversions of `time_t`.
## Ongoing work in kernel and glibc
A few people are working part time on this. Kernel patches are 60%
done after 5 years, GNU libc about 75% (but only some of those changes
have been applied). More people may be needed to speed this up and get
it finished.
The kernel side is coordinated through the y2038 mailing list:
<https://lists.linaro.org/mailman/listinfo/y2038>. Patches are all
sent to this mailing list. There is currently no git tree collecting
them all.
Help is wanted to:
* Update device drivers
* Review sound patches
* Collect patches into single git tree
The glibc side is coordinated through the general development mailing
list: <https://www.gnu.org/software/libc/involved.html>,
<https://sourceware.org/ml/libc-alpha/>.
--
Ben Hutchings
Software Developer, Codethink Ltd.
The code to check the adjtimex() or clock_adjtime() arguments is spread
out across multiple files for presumably only historic reasons. As a
preparatation for a rework to get rid of the use of 'struct timeval'
and 'struct timespec' in there, this moves all the portions into
kernel/time/timekeeping.c and marks them as 'static'.
The warp_clock() function here is not as closely related as the others,
but I feel it still makes sense to move it here in order to consolidate
all callers of timekeeping_inject_offset().
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
include/linux/time.h | 26 ----------
kernel/time/ntp.c | 61 ----------------------
kernel/time/ntp_internal.h | 1 -
kernel/time/time.c | 36 +------------
kernel/time/timekeeping.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-
kernel/time/timekeeping.h | 2 +-
6 files changed, 123 insertions(+), 126 deletions(-)
diff --git a/include/linux/time.h b/include/linux/time.h
index 9bc1f945777c..c0fbad08448f 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -134,32 +134,6 @@ 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;
-}
-
/* Some architectures do not supply their own clocksource.
* This is mainly the case in architectures that get their
* inter-tick times by reading the counter on their interval
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index edf19cc53140..a5e702669d84 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -653,67 +653,6 @@ static inline void process_adjtimex_modes(struct timex *txc,
}
-
-/**
- * ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex
- */
-int ntp_validate_timex(struct timex *txc)
-{
- if (txc->modes & ADJ_ADJTIME) {
- /* singleshot must not be used with any other mode bits */
- if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
- return -EINVAL;
- if (!(txc->modes & ADJ_OFFSET_READONLY) &&
- !capable(CAP_SYS_TIME))
- return -EPERM;
- } else {
- /* In order to modify anything, you gotta be super-user! */
- if (txc->modes && !capable(CAP_SYS_TIME))
- return -EPERM;
- /*
- * if the quartz is off by more than 10% then
- * something is VERY wrong!
- */
- if (txc->modes & ADJ_TICK &&
- (txc->tick < 900000/USER_HZ ||
- txc->tick > 1100000/USER_HZ))
- return -EINVAL;
- }
-
- if (txc->modes & ADJ_SETOFFSET) {
- /* In order to inject time, you gotta be super-user! */
- if (!capable(CAP_SYS_TIME))
- return -EPERM;
-
- if (txc->modes & ADJ_NANO) {
- struct timespec ts;
-
- ts.tv_sec = txc->time.tv_sec;
- ts.tv_nsec = txc->time.tv_usec;
- if (!timespec_inject_offset_valid(&ts))
- return -EINVAL;
-
- } else {
- if (!timeval_inject_offset_valid(&txc->time))
- return -EINVAL;
- }
- }
-
- /*
- * Check for potential multiplication overflows that can
- * only happen on 64-bit systems:
- */
- if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) {
- if (LLONG_MIN / PPM_SCALE > txc->freq)
- return -EINVAL;
- if (LLONG_MAX / PPM_SCALE < txc->freq)
- return -EINVAL;
- }
-
- return 0;
-}
-
-
/*
* adjtimex mainly allows reading (and writing, if superuser) of
* kernel time-keeping variables. used by xntpd.
diff --git a/kernel/time/ntp_internal.h b/kernel/time/ntp_internal.h
index d8a7c11fa71a..74b52cd48209 100644
--- a/kernel/time/ntp_internal.h
+++ b/kernel/time/ntp_internal.h
@@ -7,7 +7,6 @@ extern void ntp_clear(void);
extern u64 ntp_tick_length(void);
extern ktime_t ntp_get_next_leap(void);
extern int second_overflow(time64_t secs);
-extern int ntp_validate_timex(struct timex *);
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
extern void __hardpps(const struct timespec64 *, const struct timespec64 *);
#endif /* _LINUX_NTP_INTERNAL_H */
diff --git a/kernel/time/time.c b/kernel/time/time.c
index 44a8c1402133..04684e294f00 100644
--- a/kernel/time/time.c
+++ b/kernel/time/time.c
@@ -158,40 +158,6 @@ SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,
}
/*
- * Indicates if there is an offset between the system clock and the hardware
- * clock/persistent clock/rtc.
- */
-int persistent_clock_is_local;
-
-/*
- * Adjust the time obtained from the CMOS to be UTC time instead of
- * local time.
- *
- * This is ugly, but preferable to the alternatives. Otherwise we
- * would either need to write a program to do it in /etc/rc (and risk
- * confusion if the program gets run more than once; it would also be
- * hard to make the program warp the clock precisely n hours) or
- * compile in the timezone information into the kernel. Bad, bad....
- *
- * - TYT, 1992-01-01
- *
- * The best thing to do is to keep the CMOS clock in universal time (UTC)
- * as real UNIX machines always do it. This avoids all headaches about
- * daylight saving times and warping kernel clocks.
- */
-static inline void warp_clock(void)
-{
- if (sys_tz.tz_minuteswest != 0) {
- struct timespec adjust;
-
- persistent_clock_is_local = 1;
- adjust.tv_sec = sys_tz.tz_minuteswest * 60;
- adjust.tv_nsec = 0;
- timekeeping_inject_offset(&adjust);
- }
-}
-
-/*
* In case for some reason the CMOS clock has not already been running
* in UTC, but in some local time: The first time we set the timezone,
* we will warp the clock so that it is ticking UTC time instead of
@@ -224,7 +190,7 @@ int do_sys_settimeofday64(const struct timespec64 *tv, const struct timezone *tz
if (firsttime) {
firsttime = 0;
if (!tv)
- warp_clock();
+ timekeeping_warp_clock();
}
}
if (tv)
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 8af77006e937..679dbfbea419 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1300,13 +1300,39 @@ int do_settimeofday64(const struct timespec64 *ts)
}
EXPORT_SYMBOL(do_settimeofday64);
+/*
+ * 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;
+}
+
/**
* timekeeping_inject_offset - Adds or subtracts from the current time.
* @tv: pointer to the timespec variable containing the offset
*
* Adds or subtracts an offset value from the current time.
*/
-int timekeeping_inject_offset(struct timespec *ts)
+static int timekeeping_inject_offset(struct timespec *ts)
{
struct timekeeper *tk = &tk_core.timekeeper;
unsigned long flags;
@@ -1345,7 +1371,40 @@ int timekeeping_inject_offset(struct timespec *ts)
return ret;
}
-EXPORT_SYMBOL(timekeeping_inject_offset);
+
+/*
+ * Indicates if there is an offset between the system clock and the hardware
+ * clock/persistent clock/rtc.
+ */
+int persistent_clock_is_local;
+
+/*
+ * Adjust the time obtained from the CMOS to be UTC time instead of
+ * local time.
+ *
+ * This is ugly, but preferable to the alternatives. Otherwise we
+ * would either need to write a program to do it in /etc/rc (and risk
+ * confusion if the program gets run more than once; it would also be
+ * hard to make the program warp the clock precisely n hours) or
+ * compile in the timezone information into the kernel. Bad, bad....
+ *
+ * - TYT, 1992-01-01
+ *
+ * The best thing to do is to keep the CMOS clock in universal time (UTC)
+ * as real UNIX machines always do it. This avoids all headaches about
+ * daylight saving times and warping kernel clocks.
+ */
+void timekeeping_warp_clock(void)
+{
+ if (sys_tz.tz_minuteswest != 0) {
+ struct timespec adjust;
+
+ persistent_clock_is_local = 1;
+ adjust.tv_sec = sys_tz.tz_minuteswest * 60;
+ adjust.tv_nsec = 0;
+ timekeeping_inject_offset(&adjust);
+ }
+}
/**
* __timekeeping_set_tai_offset - Sets the TAI offset from UTC and monotonic
@@ -2290,6 +2349,66 @@ ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real,
}
/**
+ * ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex
+ */
+static int ntp_validate_timex(struct timex *txc)
+{
+ if (txc->modes & ADJ_ADJTIME) {
+ /* singleshot must not be used with any other mode bits */
+ if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
+ return -EINVAL;
+ if (!(txc->modes & ADJ_OFFSET_READONLY) &&
+ !capable(CAP_SYS_TIME))
+ return -EPERM;
+ } else {
+ /* In order to modify anything, you gotta be super-user! */
+ if (txc->modes && !capable(CAP_SYS_TIME))
+ return -EPERM;
+ /*
+ * if the quartz is off by more than 10% then
+ * something is VERY wrong!
+ */
+ if (txc->modes & ADJ_TICK &&
+ (txc->tick < 900000/USER_HZ ||
+ txc->tick > 1100000/USER_HZ))
+ return -EINVAL;
+ }
+
+ if (txc->modes & ADJ_SETOFFSET) {
+ /* In order to inject time, you gotta be super-user! */
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+
+ if (txc->modes & ADJ_NANO) {
+ struct timespec ts;
+
+ ts.tv_sec = txc->time.tv_sec;
+ ts.tv_nsec = txc->time.tv_usec;
+ if (!timespec_inject_offset_valid(&ts))
+ return -EINVAL;
+
+ } else {
+ if (!timeval_inject_offset_valid(&txc->time))
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Check for potential multiplication overflows that can
+ * only happen on 64-bit systems:
+ */
+ if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) {
+ if (LLONG_MIN / PPM_SCALE > txc->freq)
+ return -EINVAL;
+ if (LLONG_MAX / PPM_SCALE < txc->freq)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/**
* do_adjtimex() - Accessor function to NTP __do_adjtimex function
*/
int do_adjtimex(struct timex *txc)
diff --git a/kernel/time/timekeeping.h b/kernel/time/timekeeping.h
index d0914676d4c5..44aec7893cdd 100644
--- a/kernel/time/timekeeping.h
+++ b/kernel/time/timekeeping.h
@@ -10,7 +10,7 @@ extern ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq,
extern int timekeeping_valid_for_hres(void);
extern u64 timekeeping_max_deferment(void);
-extern int timekeeping_inject_offset(struct timespec *ts);
+extern void timekeeping_warp_clock(void);
extern int timekeeping_suspend(void);
extern void timekeeping_resume(void);
--
2.9.0
We want to remove uses of do_gettimeofday() from the kernel since the
resulting timeval structure overflows in 2038. This is not a problem for
this particular use, but do_gettimeofday() is also not an appropriate
method for measuring time intervals, since it requires a conversion into
microseconds and is complicated to work with.
ktime_get() is a better replacement, as it works with the monontonic
kernel timebase and requires a minimum of computation.
I'm slightly changing the output from microseconds to nanoseconds here,
to avoid introducing a new division operation. This should be fine
since the value is only used for debugging.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
drivers/gpu/drm/via/via_drv.h | 4 ++--
drivers/gpu/drm/via/via_irq.c | 21 +++++++--------------
2 files changed, 9 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h
index 9873942ca8f4..6d1ae834484c 100644
--- a/drivers/gpu/drm/via/via_drv.h
+++ b/drivers/gpu/drm/via/via_drv.h
@@ -74,9 +74,9 @@ typedef struct drm_via_private {
volatile uint32_t *last_pause_ptr;
volatile uint32_t *hw_addr_ptr;
drm_via_ring_buffer_t ring;
- struct timeval last_vblank;
+ ktime_t last_vblank;
int last_vblank_valid;
- unsigned usec_per_vblank;
+ ktime_t nsec_per_vblank;
atomic_t vbl_received;
drm_via_state_t hc_state;
char pci_buf[VIA_PCI_BUF_SIZE];
diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c
index ea8172c747a2..24e71578af4d 100644
--- a/drivers/gpu/drm/via/via_irq.c
+++ b/drivers/gpu/drm/via/via_irq.c
@@ -88,13 +88,6 @@ static int via_num_unichrome = ARRAY_SIZE(via_unichrome_irqs);
static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1};
-static unsigned time_diff(struct timeval *now, struct timeval *then)
-{
- return (now->tv_usec >= then->tv_usec) ?
- now->tv_usec - then->tv_usec :
- 1000000 - (then->tv_usec - now->tv_usec);
-}
-
u32 via_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
{
drm_via_private_t *dev_priv = dev->dev_private;
@@ -111,7 +104,7 @@ irqreturn_t via_driver_irq_handler(int irq, void *arg)
drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
u32 status;
int handled = 0;
- struct timeval cur_vblank;
+ ktime_t cur_vblank;
drm_via_irq_t *cur_irq = dev_priv->via_irqs;
int i;
@@ -119,18 +112,18 @@ irqreturn_t via_driver_irq_handler(int irq, void *arg)
if (status & VIA_IRQ_VBLANK_PENDING) {
atomic_inc(&dev_priv->vbl_received);
if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) {
- do_gettimeofday(&cur_vblank);
+ cur_vblank = ktime_get();
if (dev_priv->last_vblank_valid) {
- dev_priv->usec_per_vblank =
- time_diff(&cur_vblank,
- &dev_priv->last_vblank) >> 4;
+ dev_priv->nsec_per_vblank =
+ ktime_sub(cur_vblank,
+ dev_priv->last_vblank) >> 4;
}
dev_priv->last_vblank = cur_vblank;
dev_priv->last_vblank_valid = 1;
}
if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) {
- DRM_DEBUG("US per vblank is: %u\n",
- dev_priv->usec_per_vblank);
+ DRM_DEBUG("nsec per vblank is: %llu\n",
+ ktime_to_ns(dev_priv->nsec_per_vblank));
}
drm_handle_vblank(dev, 0);
handled = 1;
--
2.9.0
isofs uses a 'char' variable to load the number of years since
1900 for an inode timestamp. On architectures that use a signed
char type by default, this results in an invalid date for
anything beyond 2027.
This changes the function argument to a 'u8' array, which
is defined the same way on all architectures, and unambiguously
lets us use years until 2155.
This should be backported to all kernels that might still be
in use by that date.
Cc: stable(a)vger.kernel.org
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
v2: change function prototype instead of adding a cast
---
fs/isofs/isofs.h | 2 +-
fs/isofs/rock.h | 2 +-
fs/isofs/util.c | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h
index 133a456b0425..bd4047585431 100644
--- a/fs/isofs/isofs.h
+++ b/fs/isofs/isofs.h
@@ -106,7 +106,7 @@ static inline unsigned int isonum_733(char *p)
/* Ignore bigendian datum due to broken mastering programs */
return get_unaligned_le32(p);
}
-extern int iso_date(char *, int);
+extern int iso_date(u8 *, int);
struct inode; /* To make gcc happy */
diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h
index ed09e2b08637..f835976ce033 100644
--- a/fs/isofs/rock.h
+++ b/fs/isofs/rock.h
@@ -65,7 +65,7 @@ struct RR_PL_s {
};
struct stamp {
- char time[7];
+ __u8 time[7]; /* actually 6 unsigned, 1 signed */
} __attribute__ ((packed));
struct RR_TF_s {
diff --git a/fs/isofs/util.c b/fs/isofs/util.c
index 005a15cfd30a..335f62db0b53 100644
--- a/fs/isofs/util.c
+++ b/fs/isofs/util.c
@@ -15,12 +15,12 @@
* to GMT. Thus we should always be correct.
*/
-int iso_date(char * p, int flag)
+int iso_date(u8 *p, int flag)
{
int year, month, day, hour, minute, second, tz;
int crtime;
- year = p[0];
+ year = (int)(u8)p[0];
month = p[1];
day = p[2];
hour = p[3];
--
2.9.0