KVM reads the current boottime value as a struct timespec in order to
calculate the guest wallclock time, resulting in an overflow in 2038
on 32-bit systems.
The data then gets passed as an unsigned 32-bit number to the guest,
and that in turn overflows in 2106.
We cannot do much about the second overflow, which affects both 32-bit
and 64-bit hosts, but we can ensure that they both behave the same
way and don't overflow until 2106, by using getboottime64() to read
a timespec64 value.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
arch/x86/kvm/x86.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 53241618e3c9..f79c86510408 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1163,7 +1163,7 @@ static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)
int version;
int r;
struct pvclock_wall_clock wc;
- struct timespec boot;
+ struct timespec64 boot;
if (!wall_clock)
return;
@@ -1186,13 +1186,13 @@ static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)
* wall clock specified here. guest system time equals host
* system time for us, thus we must fill in host boot time here.
*/
- getboottime(&boot);
+ getboottime64(&boot);
if (kvm->arch.kvmclock_offset) {
- struct timespec ts = ns_to_timespec(kvm->arch.kvmclock_offset);
- boot = timespec_sub(boot, ts);
+ struct timespec64 ts = ns_to_timespec64(kvm->arch.kvmclock_offset);
+ boot = timespec64_sub(boot, ts);
}
- wc.sec = boot.tv_sec;
+ wc.sec = (u32)boot.tv_sec; /* overflow in 2106 guest time */
wc.nsec = boot.tv_nsec;
wc.version = version;
--
2.9.0
As part of the Y2038 development, __getnstimeofday is not supposed to be
used any more. It is now replaced with ktime_get_raw_ns. Albeit
ktime_get_raw_ns is monotonic compared to __getnstimeofday, this
difference is irrelevant as the Jitter RNG uses the time stamp to
measure the execution time of a given code path and tries to detect
variations in the execution time. Therefore, the only requirement the
Jitter RNG has, is a sufficient high resolution to detect these
variations.
The change was tested on x86 to show an identical behavior as RDTSC. The
used test code simply measures the execution time of the heart of the
RNG:
jent_get_nstime(&time);
jent_memaccess(ec, min);
jent_fold_time(NULL, time, &folded, min);
jent_get_nstime(&time2);
return ((time2 - time));
Signed-off-by: Stephan Mueller <smueller(a)chronox.de>
---
crypto/jitterentropy-kcapi.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/crypto/jitterentropy-kcapi.c b/crypto/jitterentropy-kcapi.c
index 597cedd..69a2988 100644
--- a/crypto/jitterentropy-kcapi.c
+++ b/crypto/jitterentropy-kcapi.c
@@ -87,24 +87,29 @@ void jent_memcpy(void *dest, const void *src, unsigned int n)
memcpy(dest, src, n);
}
+/*
+ * Obtain a high-resolution time stamp value. The time stamp is used to measure
+ * the execution time of a given code path and its variations. Hence, the time
+ * stamp must have a sufficiently high resolution. It is valid if the time
+ * runs backwards for short period of time as the RNG code is able handle that.
+ *
+ * Note, if the function returns zero because a given architecture does not
+ * implement a high-resolution time stamp, the RNG code's runtime test
+ * will detect it and will not produce output.
+ */
void jent_get_nstime(__u64 *out)
{
- struct timespec ts;
__u64 tmp = 0;
tmp = random_get_entropy();
/*
- * If random_get_entropy does not return a value (which is possible on,
- * for example, MIPS), invoke __getnstimeofday
+ * If random_get_entropy does not return a value, i.e. it is not
+ * implemented for a given architecture, invoke ktime_get_raw_ns
* hoping that there are timers we can work with.
*/
- if ((0 == tmp) &&
- (0 == __getnstimeofday(&ts))) {
- tmp = ts.tv_sec;
- tmp = tmp << 32;
- tmp = tmp | ts.tv_nsec;
- }
+ if (tmp == 0)
+ tmp = ktime_get_raw_ns();
*out = tmp;
}
--
2.5.5
The jent_get_nstime() function uses __getnstimeofday() to get
something similar to a 64-bit nanosecond counter. As we want
to get rid of struct timespec to fix the y2038 overflow,
this patch changes the code to use __getnstimeofday64()
instead, which returns a timespec64 structure.
Nothing changes about the algorithm, but it looks like it
might be better to use
*out = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
or even
*out = ktime_get_raw_fast_ns();
to get an actual nanosecond value and avoid the predictable
jitter that happens at the end of a second. Checking whether
or not this would be good needs investigation by someone who
understands the code better than me.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
crypto/jitterentropy-kcapi.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/crypto/jitterentropy-kcapi.c b/crypto/jitterentropy-kcapi.c
index 597cedd3531c..82ac44eff20d 100644
--- a/crypto/jitterentropy-kcapi.c
+++ b/crypto/jitterentropy-kcapi.c
@@ -89,7 +89,7 @@ void jent_memcpy(void *dest, const void *src, unsigned int n)
void jent_get_nstime(__u64 *out)
{
- struct timespec ts;
+ struct timespec64 ts;
__u64 tmp = 0;
tmp = random_get_entropy();
@@ -98,9 +98,11 @@ void jent_get_nstime(__u64 *out)
* If random_get_entropy does not return a value (which is possible on,
* for example, MIPS), invoke __getnstimeofday
* hoping that there are timers we can work with.
+ *
+ * should we have a __ktime_get_ns() instead?
*/
if ((0 == tmp) &&
- (0 == __getnstimeofday(&ts))) {
+ (0 == __getnstimeofday64(&ts))) {
tmp = ts.tv_sec;
tmp = tmp << 32;
tmp = tmp | ts.tv_nsec;
--
2.9.0
The fc_get_host_stats() function contains a complex conversion
from jiffies to timespec to seconds. As we try to get rid of
uses of struct timespec, we can clean this up and replace it
with a simpler computation.
Simply dividing the difference in jiffies by HZ is not only
much more efficient, it also avoids a problem that causes the
seconds_since_last_reset value to be incorrect if jiffies has
overrun since the 'boot_time' value was recorded.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
drivers/scsi/libfc/fc_lport.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index e01a29863c38..99c6d9251404 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -301,7 +301,6 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
{
struct fc_host_statistics *fc_stats;
struct fc_lport *lport = shost_priv(shost);
- struct timespec v0, v1;
unsigned int cpu;
u64 fcp_in_bytes = 0;
u64 fcp_out_bytes = 0;
@@ -309,9 +308,7 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
fc_stats = &lport->host_stats;
memset(fc_stats, 0, sizeof(struct fc_host_statistics));
- jiffies_to_timespec(jiffies, &v0);
- jiffies_to_timespec(lport->boot_time, &v1);
- fc_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
+ fc_stats->seconds_since_last_reset = (lport->boot_time - jiffies) / HZ;
for_each_possible_cpu(cpu) {
struct fc_stats *stats;
--
2.9.0
Jeff Moyer looked up the blktrace source to see if an overflow might
happen. The situation is as follows:
- The time stamp is not used by the program itself, only for
printing human-readable output.
- We normally don't print the timestamp at all, except when an
undocumented format option is given to blkparse.
- The assumption is that no other program besides blktrace
even looks at this data, but of course cannot be sure.
- On 64-bit systems, the time gets read from the unsigned
32-bit kernel structure into a timespec in a way that will
work correctly until 2106, so there is no 2038 problem.
- On 32-bit systems that have a new (future) libc build with
a 64-bit time_t type, it will work the same way.
- On current 32-bit systems, the time is passed into localtime(),
at which point the overflow happens, but those systems are
already broken.
In short, it's good enough for now, so update the comment.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
Fixes: 59a37f8baeb2 ("blktrace: avoid using timespec")
Cc: Jeff Moyer <jmoyer(a)redhat.com>
---
kernel/trace/blktrace.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index b0816e4a61a5..4a3666779589 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -131,7 +131,8 @@ static void trace_note_time(struct blk_trace *bt)
unsigned long flags;
u32 words[2];
- /* need to check user space to see if this breaks in y2038 or y2106 */
+ /* blktrace converts this to a time_t and will overflow in
+ 2106, not in 2038 */
ktime_get_real_ts64(&now);
words[0] = (u32)now.tv_sec;
words[1] = now.tv_nsec;
--
2.9.0
The blktrace code stores the current time in a 32-bit word in its
user interface. This is a bad idea because 32-bit seconds overflow
at some point.
We probably have until 2106 before this one overflows, as it seems
to use an 'unsigned' variable, but we should confirm that user
space treats it the same way.
Aside from this, we want to stop using 'struct timespec' here,
so I'm adding a comment about the overflow and change the code
to use timespec64 instead to make the loss of range more obvious.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
kernel/trace/blktrace.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index ef86b965ade3..b0816e4a61a5 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -127,12 +127,13 @@ static void trace_note_tsk(struct task_struct *tsk)
static void trace_note_time(struct blk_trace *bt)
{
- struct timespec now;
+ struct timespec64 now;
unsigned long flags;
u32 words[2];
- getnstimeofday(&now);
- words[0] = now.tv_sec;
+ /* need to check user space to see if this breaks in y2038 or y2106 */
+ ktime_get_real_ts64(&now);
+ words[0] = (u32)now.tv_sec;
words[1] = now.tv_nsec;
local_irq_save(flags);
--
2.9.0
The conversion to the 64-bit time based ptp methods left two instances
of 'struct timespec' in place. This is harmless because 64-bit
architectures define timespec64 as timespec, and this driver is
not used on 32-bit machines.
However, using 'struct timespec64' directly is obviously the right
thing to do, and will help us remove 'struct timespec' in the future.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
Fixes: b9acf24f779c ("ptp: tilegx: convert to the 64 bit get/set time methods.")
---
drivers/net/ethernet/tile/tilegx.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c
index 0a15acc075b3..11213a38c795 100644
--- a/drivers/net/ethernet/tile/tilegx.c
+++ b/drivers/net/ethernet/tile/tilegx.c
@@ -462,7 +462,7 @@ static void tile_tx_timestamp(struct sk_buff *skb, int instance)
if (unlikely((shtx->tx_flags & SKBTX_HW_TSTAMP) != 0)) {
struct mpipe_data *md = &mpipe_data[instance];
struct skb_shared_hwtstamps shhwtstamps;
- struct timespec ts;
+ struct timespec64 ts;
shtx->tx_flags |= SKBTX_IN_PROGRESS;
gxio_mpipe_get_timestamp(&md->context, &ts);
@@ -886,9 +886,9 @@ static struct ptp_clock_info ptp_mpipe_caps = {
/* Sync mPIPE's timestamp up with Linux system time and register PTP clock. */
static void register_ptp_clock(struct net_device *dev, struct mpipe_data *md)
{
- struct timespec ts;
+ struct timespec64 ts;
- getnstimeofday(&ts);
+ ktime_get_ts64(&ts);
gxio_mpipe_set_timestamp(&md->context, &ts);
mutex_init(&md->ptp_lock);
--
2.9.0