| From a4f8f6667f099036c88f231dcad4cf233652c824 Mon Sep 17 00:00:00 2001 |
| From: John Stultz <john.stultz@linaro.org> |
| Date: Tue, 23 Aug 2016 16:08:22 -0700 |
| Subject: timekeeping: Cap array access in timekeeping_debug |
| |
| From: John Stultz <john.stultz@linaro.org> |
| |
| commit a4f8f6667f099036c88f231dcad4cf233652c824 upstream. |
| |
| It was reported that hibernation could fail on the 2nd attempt, where the |
| system hangs at hibernate() -> syscore_resume() -> i8237A_resume() -> |
| claim_dma_lock(), because the lock has already been taken. |
| |
| However there is actually no other process would like to grab this lock on |
| that problematic platform. |
| |
| Further investigation showed that the problem is triggered by setting |
| /sys/power/pm_trace to 1 before the 1st hibernation. |
| |
| Since once pm_trace is enabled, the rtc becomes unmeaningful after suspend, |
| and meanwhile some BIOSes would like to adjust the 'invalid' RTC (e.g, smaller |
| than 1970) to the release date of that motherboard during POST stage, thus |
| after resumed, it may seem that the system had a significant long sleep time |
| which is a completely meaningless value. |
| |
| Then in timekeeping_resume -> tk_debug_account_sleep_time, if the bit31 of the |
| sleep time happened to be set to 1, fls() returns 32 and we add 1 to |
| sleep_time_bin[32], which causes an out of bounds array access and therefor |
| memory being overwritten. |
| |
| As depicted by System.map: |
| 0xffffffff81c9d080 b sleep_time_bin |
| 0xffffffff81c9d100 B dma_spin_lock |
| the dma_spin_lock.val is set to 1, which caused this problem. |
| |
| This patch adds a sanity check in tk_debug_account_sleep_time() |
| to ensure we don't index past the sleep_time_bin array. |
| |
| [jstultz: Problem diagnosed and original patch by Chen Yu, I've solved the |
| issue slightly differently, but borrowed his excelent explanation of the |
| issue here.] |
| |
| Fixes: 5c83545f24ab "power: Add option to log time spent in suspend" |
| Reported-by: Janek Kozicki <cosurgi@gmail.com> |
| Reported-by: Chen Yu <yu.c.chen@intel.com> |
| Signed-off-by: John Stultz <john.stultz@linaro.org> |
| Cc: linux-pm@vger.kernel.org |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Xunlei Pang <xpang@redhat.com> |
| Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net> |
| Cc: Zhang Rui <rui.zhang@intel.com> |
| Link: http://lkml.kernel.org/r/1471993702-29148-3-git-send-email-john.stultz@linaro.org |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/time/timekeeping_debug.c | 9 +++++++-- |
| 1 file changed, 7 insertions(+), 2 deletions(-) |
| |
| --- a/kernel/time/timekeeping_debug.c |
| +++ b/kernel/time/timekeeping_debug.c |
| @@ -23,7 +23,9 @@ |
| |
| #include "timekeeping_internal.h" |
| |
| -static unsigned int sleep_time_bin[32] = {0}; |
| +#define NUM_BINS 32 |
| + |
| +static unsigned int sleep_time_bin[NUM_BINS] = {0}; |
| |
| static int tk_debug_show_sleep_time(struct seq_file *s, void *data) |
| { |
| @@ -69,6 +71,9 @@ late_initcall(tk_debug_sleep_time_init); |
| |
| void tk_debug_account_sleep_time(struct timespec64 *t) |
| { |
| - sleep_time_bin[fls(t->tv_sec)]++; |
| + /* Cap bin index so we don't overflow the array */ |
| + int bin = min(fls(t->tv_sec), NUM_BINS-1); |
| + |
| + sleep_time_bin[bin]++; |
| } |
| |