Commit 152194fe authored by Deepa Dinamani's avatar Deepa Dinamani Committed by Dmitry Torokhov

Input: extend usable life of event timestamps to 2106 on 32 bit systems

The input events use struct timeval to store event time, unfortunately
this structure is not y2038 safe and is being replaced in kernel with
y2038 safe structures.

Because of ABI concerns we can not change the size or the layout of
structure input_event, so we opt to re-interpreting the 'seconds' part
of timestamp as an unsigned value, effectively doubling the range of
values, to year 2106.

Newer glibc that has support for 32 bit applications to use 64 bit
time_t supplies __USE_TIME_BITS64 define [1], that we can use to present
the userspace with updated input_event layout. The updated layout will
cause the compile time breakage, alerting applications and distributions
maintainers to the issue. Existing 32 binaries will continue working
without any changes until 2038.

Ultimately userspace applications should switch to using monotonic or
boot time clocks, as realtime clock is not very well suited for input
event timestamps as it can go backwards (see a80b83b7 "Input: evdev -
add CLOCK_BOOTTIME support" by by John Stultz). With monotonic clock the
practical range of reported times will always fit into the pair of 32
bit values, as we do not expect any system to stay up for a hundred
years without a single reboot.

[1] https://sourceware.org/glibc/wiki/Y2038ProofnessDesignSuggested-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarDeepa Dinamani <deepa.kernel@gmail.com>
Acked-by: default avatarPeter Hutterer <peter.hutterer@who-t.net>
Patchwork-Id: 10148083
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent eca3be9b
...@@ -135,10 +135,7 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) ...@@ -135,10 +135,7 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
continue; continue;
} else if (head != i) { } else if (head != i) {
/* move entry to fill the gap */ /* move entry to fill the gap */
client->buffer[head].time = ev->time; client->buffer[head] = *ev;
client->buffer[head].type = ev->type;
client->buffer[head].code = ev->code;
client->buffer[head].value = ev->value;
} }
num++; num++;
...@@ -157,6 +154,7 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client) ...@@ -157,6 +154,7 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client)
{ {
struct input_event ev; struct input_event ev;
ktime_t time; ktime_t time;
struct timespec64 ts;
time = client->clk_type == EV_CLK_REAL ? time = client->clk_type == EV_CLK_REAL ?
ktime_get_real() : ktime_get_real() :
...@@ -164,7 +162,9 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client) ...@@ -164,7 +162,9 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client)
ktime_get() : ktime_get() :
ktime_get_boottime(); ktime_get_boottime();
ev.time = ktime_to_timeval(time); ts = ktime_to_timespec64(time);
ev.input_event_sec = ts.tv_sec;
ev.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
ev.type = EV_SYN; ev.type = EV_SYN;
ev.code = SYN_DROPPED; ev.code = SYN_DROPPED;
ev.value = 0; ev.value = 0;
...@@ -241,7 +241,10 @@ static void __pass_event(struct evdev_client *client, ...@@ -241,7 +241,10 @@ static void __pass_event(struct evdev_client *client,
*/ */
client->tail = (client->head - 2) & (client->bufsize - 1); client->tail = (client->head - 2) & (client->bufsize - 1);
client->buffer[client->tail].time = event->time; client->buffer[client->tail].input_event_sec =
event->input_event_sec;
client->buffer[client->tail].input_event_usec =
event->input_event_usec;
client->buffer[client->tail].type = EV_SYN; client->buffer[client->tail].type = EV_SYN;
client->buffer[client->tail].code = SYN_DROPPED; client->buffer[client->tail].code = SYN_DROPPED;
client->buffer[client->tail].value = 0; client->buffer[client->tail].value = 0;
...@@ -262,12 +265,15 @@ static void evdev_pass_values(struct evdev_client *client, ...@@ -262,12 +265,15 @@ static void evdev_pass_values(struct evdev_client *client,
struct evdev *evdev = client->evdev; struct evdev *evdev = client->evdev;
const struct input_value *v; const struct input_value *v;
struct input_event event; struct input_event event;
struct timespec64 ts;
bool wakeup = false; bool wakeup = false;
if (client->revoked) if (client->revoked)
return; return;
event.time = ktime_to_timeval(ev_time[client->clk_type]); ts = ktime_to_timespec64(ev_time[client->clk_type]);
event.input_event_sec = ts.tv_sec;
event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
/* Interrupts are disabled, just acquire the lock. */ /* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock); spin_lock(&client->buffer_lock);
......
...@@ -24,8 +24,8 @@ int input_event_from_user(const char __user *buffer, ...@@ -24,8 +24,8 @@ int input_event_from_user(const char __user *buffer,
sizeof(struct input_event_compat))) sizeof(struct input_event_compat)))
return -EFAULT; return -EFAULT;
event->time.tv_sec = compat_event.time.tv_sec; event->input_event_sec = compat_event.sec;
event->time.tv_usec = compat_event.time.tv_usec; event->input_event_usec = compat_event.usec;
event->type = compat_event.type; event->type = compat_event.type;
event->code = compat_event.code; event->code = compat_event.code;
event->value = compat_event.value; event->value = compat_event.value;
...@@ -44,8 +44,8 @@ int input_event_to_user(char __user *buffer, ...@@ -44,8 +44,8 @@ int input_event_to_user(char __user *buffer,
if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) { if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event; struct input_event_compat compat_event;
compat_event.time.tv_sec = event->time.tv_sec; compat_event.sec = event->input_event_sec;
compat_event.time.tv_usec = event->time.tv_usec; compat_event.usec = event->input_event_usec;
compat_event.type = event->type; compat_event.type = event->type;
compat_event.code = event->code; compat_event.code = event->code;
compat_event.value = event->value; compat_event.value = event->value;
......
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
struct input_event_compat { struct input_event_compat {
struct compat_timeval time; compat_ulong_t sec;
compat_ulong_t usec;
__u16 type; __u16 type;
__u16 code; __u16 code;
__s32 value; __s32 value;
......
...@@ -90,8 +90,8 @@ static int uinput_dev_event(struct input_dev *dev, ...@@ -90,8 +90,8 @@ static int uinput_dev_event(struct input_dev *dev,
udev->buff[udev->head].code = code; udev->buff[udev->head].code = code;
udev->buff[udev->head].value = value; udev->buff[udev->head].value = value;
ktime_get_ts64(&ts); ktime_get_ts64(&ts);
udev->buff[udev->head].time.tv_sec = ts.tv_sec; udev->buff[udev->head].input_event_sec = ts.tv_sec;
udev->buff[udev->head].time.tv_usec = ts.tv_nsec / NSEC_PER_USEC; udev->buff[udev->head].input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
wake_up_interruptible(&udev->waitq); wake_up_interruptible(&udev->waitq);
......
...@@ -21,10 +21,21 @@ ...@@ -21,10 +21,21 @@
/* /*
* The event structure itself * The event structure itself
* Note that __USE_TIME_BITS64 is defined by libc based on
* application's request to use 64 bit time_t.
*/ */
struct input_event { struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL)
struct timeval time; struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
__kernel_ulong_t __sec;
__kernel_ulong_t __usec;
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type; __u16 type;
__u16 code; __u16 code;
__s32 value; __s32 value;
......
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