The firmware timestamp is an unsigned 32-bit value, but we copy it into
a signed 32-bit variable, so we can theoretically get an overflow in
the calculation when the timestamp is between 2038 and 2106.
This changes the temporary variable to time64_t and changes the deprecated
time_to_tm() over to time64_to_tm() accordingly.
There is still an overflow in y2106, but that is a limitation of the
firmware interface, not a kernel problem.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
drivers/firmware/raspberrypi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
index dd506cd3a5b8..6692888f04cf 100644
--- a/drivers/firmware/raspberrypi.c
+++ b/drivers/firmware/raspberrypi.c
@@ -174,7 +174,7 @@ rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
if (ret == 0) {
struct tm tm;
- time_to_tm(packet, 0, &tm);
+ time64_to_tm(packet, 0, &tm);
dev_info(fw->cl.dev,
"Attached to firmware from %04ld-%02d-%02d %02d:%02d\n",
--
2.9.0
struct timespec should no longer be used because of the y2038
overflow problem. This code does not suffer from the overflow,
but it's trivial to change it to use timespec64 without changing
the interface, so let's do that.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
drivers/hsi/clients/cmt_speech.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c
index 727f968ac1cb..05b80723d39d 100644
--- a/drivers/hsi/clients/cmt_speech.c
+++ b/drivers/hsi/clients/cmt_speech.c
@@ -451,11 +451,11 @@ static void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd);
cs_release_cmd(msg);
if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) {
- struct timespec tspec;
+ struct timespec64 tspec;
struct cs_timestamp *tstamp =
&hi->mmap_cfg->tstamp_rx_ctrl;
- ktime_get_ts(&tspec);
+ ktime_get_ts64(&tspec);
tstamp->tv_sec = (__u32) tspec.tv_sec;
tstamp->tv_nsec = (__u32) tspec.tv_nsec;
--
2.9.0
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