Blob Blame History Raw
From: Takashi Iwai <tiwai@suse.de>
Subject: kABI workaround for posix-timers it_overrun 64bit changes
Patch-mainline: Never, kABI fix
References: CVE-2018-12896,bsc#1099922

This is a very messy workaround for kABI breakage due to the change to
64bit values of it_overrun and it_overrun_last in struct k_itimer.
We just keep the shadow copy of 32bit values in the original place,
updating them along with 64bit values.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

---
 include/linux/posix-timers.h   |   16 ++++++++++++++--
 kernel/time/alarmtimer.c       |    1 +
 kernel/time/posix-cpu-timers.c |    6 ++++++
 kernel/time/posix-timers.c     |    9 ++++++++-
 4 files changed, 29 insertions(+), 3 deletions(-)

--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -55,8 +55,13 @@ struct k_itimer {
 	spinlock_t it_lock;
 	clockid_t it_clock;		/* which timer type */
 	timer_t it_id;			/* timer id */
-	s64 it_overrun;			/* overrun on pending signal  */
-	s64 it_overrun_last;		/* overrun on last delivered signal */
+#ifdef __GENKSYMS__
+	int it_overrun;			/* overrun on pending signal  */
+	int it_overrun_last;		/* overrun on last delivered signal */
+#else
+	int __it_overrun;		/* shadow copy of 32bit it_overrun */
+	int __it_overrun_last;		/* shadow copy of 32bit it_overrun_last */
+#endif
 	int it_requeue_pending;		/* waiting to requeue this timer */
 #define REQUEUE_PENDING 1
 	int it_sigev_notify;		/* notify word of sigevent struct */
@@ -84,6 +89,13 @@ struct k_itimer {
 		} alarm;
 		struct rcu_head rcu;
 	} it;
+#ifndef __GENKSYMS__
+	/* real values of it_overrun and it_overrun_last are kept here for
+	 * kABI compatibility
+	 */
+	s64 it_overrun;			/* overrun on pending signal  */
+	s64 it_overrun_last;		/* overrun on last delivered signal */
+#endif
 };
 
 struct k_clock {
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -380,6 +380,8 @@ static void schedule_next_timer(struct k
 
 	timr->it_overrun_last = timr->it_overrun;
 	timr->it_overrun = -1LL;
+	timr->__it_overrun_last = (unsigned int)timr->it_overrun_last;
+	timr->__it_overrun = -1;
 	++timr->it_requeue_pending;
 	hrtimer_restart(timer);
 }
@@ -505,6 +507,7 @@ static enum hrtimer_restart posix_timer_
 #endif
 			timr->it_overrun += hrtimer_forward(timer, now,
 							    timr->it.real.interval);
+			timr->__it_overrun = (unsigned int)timr->it_overrun;
 			ret = HRTIMER_RESTART;
 			++timr->it_requeue_pending;
 		}
@@ -654,6 +657,7 @@ SYSCALL_DEFINE3(timer_create, const cloc
 	new_timer->it_id = (timer_t) new_timer_id;
 	new_timer->it_clock = which_clock;
 	new_timer->it_overrun = -1LL;
+	new_timer->__it_overrun = -1;
 
 	if (timer_event_spec) {
 		if (copy_from_user(&event, timer_event_spec, sizeof (event))) {
@@ -781,8 +785,10 @@ common_timer_get(struct k_itimer *timr,
 	 * expiry is > now.
 	 */
 	if (iv && (timr->it_requeue_pending & REQUEUE_PENDING ||
-		   timr->it_sigev_notify == SIGEV_NONE))
+		   timr->it_sigev_notify == SIGEV_NONE)) {
 		timr->it_overrun += hrtimer_forward(timer, now, iv);
+		timr->__it_overrun = (unsigned int)timr->it_overrun;
+	}
 
 	remaining = __hrtimer_expires_remaining_adjusted(timer, now);
 	/* Return 0 only, when the timer is expired and not pending */
@@ -876,6 +882,7 @@ common_timer_set(struct k_itimer *timr,
 	timr->it_requeue_pending = (timr->it_requeue_pending + 2) & 
 		~REQUEUE_PENDING;
 	timr->it_overrun_last = 0;
+	timr->__it_overrun_last = 0;
 
 	/* switch off the timer when it_value is zero */
 	if (!new_setting->it_value.tv_sec && !new_setting->it_value.tv_nsec)
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -529,6 +529,7 @@ static enum alarmtimer_restart alarm_han
 						ptr->it.alarm.interval);
 		result = ALARMTIMER_RESTART;
 	}
+	ptr->__it_overrun = (unsigned int)ptr->it_overrun;
 	spin_unlock_irqrestore(&ptr->it_lock, flags);
 
 	return result;
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -79,6 +79,7 @@ static void bump_cpu_timer(struct k_itim
 
 		timer->it.cpu.expires += incr;
 		timer->it_overrun += 1LL << i;
+		timer->__it_overrun = (unsigned int)timer->it_overrun;
 		delta -= incr;
 	}
 }
@@ -682,6 +683,8 @@ static int posix_cpu_timer_set(struct k_
 		~REQUEUE_PENDING;
 	timer->it_overrun_last = 0;
 	timer->it_overrun = -1LL;
+	timer->__it_overrun_last = 0;
+	timer->__it_overrun = -1;
 
 	if (new_expires != 0 && !(val < new_expires)) {
 		/*
@@ -1034,6 +1037,8 @@ void posix_cpu_timer_schedule(struct k_i
 out:
 	timer->it_overrun_last = timer->it_overrun;
 	timer->it_overrun = -1LL;
+	timer->__it_overrun_last = (unsigned int)timer->it_overrun_last;
+	timer->__it_overrun = -1;
 	++timer->it_requeue_pending;
 }
 
@@ -1239,6 +1244,7 @@ static int do_cpu_nanosleep(const clocki
 	spin_lock_init(&timer.it_lock);
 	timer.it_clock = which_clock;
 	timer.it_overrun = -1LL;
+	timer.__it_overrun = -1;
 	error = posix_cpu_timer_create(&timer);
 	timer.it_process = current;
 	if (!error) {