| From 35dac27cedd14c3b6fcd4ba7bc3c31738cfd1831 Mon Sep 17 00:00:00 2001 |
| From: Roland Dreier <roland@purestorage.com> |
| Date: Fri, 4 Jan 2013 15:35:50 -0800 |
| Subject: printk: fix incorrect length from print_time() when seconds > 99999 |
| |
| From: Roland Dreier <roland@purestorage.com> |
| |
| commit 35dac27cedd14c3b6fcd4ba7bc3c31738cfd1831 upstream. |
| |
| print_prefix() passes a NULL buf to print_time() to get the length of |
| the time prefix; when printk times are enabled, the current code just |
| returns the constant 15, which matches the format "[%5lu.%06lu] " used |
| to print the time value. However, this is obviously incorrect when the |
| whole seconds part of the time gets beyond 5 digits (100000 seconds is a |
| bit more than a day of uptime). |
| |
| The simple fix is to use snprintf(NULL, 0, ...) to calculate the actual |
| length of the time prefix. This could be micro-optimized but it seems |
| better to have simpler, more readable code here. |
| |
| The bug leads to the syslog system call miscomputing which messages fit |
| into the userspace buffer. If there are enough messages to fill |
| log_buf_len and some have a timestamp >= 100000, dmesg may fail with: |
| |
| # dmesg |
| klogctl: Bad address |
| |
| When this happens, strace shows that the failure is indeed EFAULT due to |
| the kernel mistakenly accessing past the end of dmesg's buffer, since |
| dmesg asks the kernel how big a buffer it needs, allocates a bit more, |
| and then gets an error when it asks the kernel to fill it: |
| |
| syslog(0xa, 0, 0) = 1048576 |
| mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa4d25d2000 |
| syslog(0x3, 0x7fa4d25d2010, 0x100008) = -1 EFAULT (Bad address) |
| |
| As far as I can see, the bug has been there as long as print_time(), |
| which comes from commit 084681d14e42 ("printk: flush continuation lines |
| immediately to console") in 3.5-rc5. |
| |
| Signed-off-by: Roland Dreier <roland@purestorage.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Cc: Joe Perches <joe@perches.com> |
| Cc: Sylvain Munaut <s.munaut@whatever-company.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/printk.c | 5 +++-- |
| 1 file changed, 3 insertions(+), 2 deletions(-) |
| |
| --- a/kernel/printk.c |
| +++ b/kernel/printk.c |
| @@ -847,10 +847,11 @@ static size_t print_time(u64 ts, char *b |
| if (!printk_time) |
| return 0; |
| |
| + rem_nsec = do_div(ts, 1000000000); |
| + |
| if (!buf) |
| - return 15; |
| + return snprintf(NULL, 0, "[%5lu.000000] ", (unsigned long)ts); |
| |
| - rem_nsec = do_div(ts, 1000000000); |
| return sprintf(buf, "[%5lu.%06lu] ", |
| (unsigned long)ts, rem_nsec / 1000); |
| } |