diff options
author | Kay Sievers <kay.sievers@vrfy.org> | 2011-03-12 21:19:51 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-03-14 11:49:16 -0400 |
commit | 9d90c8d9cde929cbc575098e825d7c29d9f45054 (patch) | |
tree | 49cd83812a9cbbb40cb0c6b73b13455cad0029ea | |
parent | 54fad532b29aa9145f12a83f4f82820c2f23b4e2 (diff) |
printk: do not mangle valid userspace syslog prefixes
printk: do not mangle valid userspace syslog prefixes with /dev/kmsg
Log messages passed to the kernel log by using /dev/kmsg or /dev/ttyprintk
might contain a syslog prefix including the syslog facility value.
This makes printk to recognize these headers properly, extract the real log
level from it to use, and add the prefix as a proper prefix to the
log buffer, instead of wrongly printing it as the log message text.
Before:
$ echo '<14>text' > /dev/kmsg
$ dmesg -r
<4>[135159.594810] <14>text
After:
$ echo '<14>text' > /dev/kmsg
$ dmesg -r
<14>[ 50.750654] text
Cc: Lennart Poettering <lennart@poettering.net>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | kernel/printk.c | 138 |
1 files changed, 104 insertions, 34 deletions
diff --git a/kernel/printk.c b/kernel/printk.c index 2ddbdc73aade..5e3d042e7001 100644 --- a/kernel/printk.c +++ b/kernel/printk.c | |||
@@ -499,6 +499,71 @@ static void _call_console_drivers(unsigned start, | |||
499 | } | 499 | } |
500 | 500 | ||
501 | /* | 501 | /* |
502 | * Parse the syslog header <[0-9]*>. The decimal value represents 32bit, the | ||
503 | * lower 3 bit are the log level, the rest are the log facility. In case | ||
504 | * userspace passes usual userspace syslog messages to /dev/kmsg or | ||
505 | * /dev/ttyprintk, the log prefix might contain the facility. Printk needs | ||
506 | * to extract the correct log level for in-kernel processing, and not mangle | ||
507 | * the original value. | ||
508 | * | ||
509 | * If a prefix is found, the length of the prefix is returned. If 'level' is | ||
510 | * passed, it will be filled in with the log level without a possible facility | ||
511 | * value. If 'special' is passed, the special printk prefix chars are accepted | ||
512 | * and returned. If no valid header is found, 0 is returned and the passed | ||
513 | * variables are not touched. | ||
514 | */ | ||
515 | static size_t log_prefix(const char *p, unsigned int *level, char *special) | ||
516 | { | ||
517 | unsigned int lev = 0; | ||
518 | char sp = '\0'; | ||
519 | size_t len; | ||
520 | |||
521 | if (p[0] != '<' || !p[1]) | ||
522 | return 0; | ||
523 | if (p[2] == '>') { | ||
524 | /* usual single digit level number or special char */ | ||
525 | switch (p[1]) { | ||
526 | case '0' ... '7': | ||
527 | lev = p[1] - '0'; | ||
528 | break; | ||
529 | case 'c': /* KERN_CONT */ | ||
530 | case 'd': /* KERN_DEFAULT */ | ||
531 | sp = p[1]; | ||
532 | break; | ||
533 | default: | ||
534 | return 0; | ||
535 | } | ||
536 | len = 3; | ||
537 | } else { | ||
538 | /* multi digit including the level and facility number */ | ||
539 | char *endp = NULL; | ||
540 | |||
541 | if (p[1] < '0' && p[1] > '9') | ||
542 | return 0; | ||
543 | |||
544 | lev = (simple_strtoul(&p[1], &endp, 10) & 7); | ||
545 | if (endp == NULL || endp[0] != '>') | ||
546 | return 0; | ||
547 | len = (endp + 1) - p; | ||
548 | } | ||
549 | |||
550 | /* do not accept special char if not asked for */ | ||
551 | if (sp && !special) | ||
552 | return 0; | ||
553 | |||
554 | if (special) { | ||
555 | *special = sp; | ||
556 | /* return special char, do not touch level */ | ||
557 | if (sp) | ||
558 | return len; | ||
559 | } | ||
560 | |||
561 | if (level) | ||
562 | *level = lev; | ||
563 | return len; | ||
564 | } | ||
565 | |||
566 | /* | ||
502 | * Call the console drivers, asking them to write out | 567 | * Call the console drivers, asking them to write out |
503 | * log_buf[start] to log_buf[end - 1]. | 568 | * log_buf[start] to log_buf[end - 1]. |
504 | * The console_lock must be held. | 569 | * The console_lock must be held. |
@@ -513,13 +578,9 @@ static void call_console_drivers(unsigned start, unsigned end) | |||
513 | cur_index = start; | 578 | cur_index = start; |
514 | start_print = start; | 579 | start_print = start; |
515 | while (cur_index != end) { | 580 | while (cur_index != end) { |
516 | if (msg_level < 0 && ((end - cur_index) > 2) && | 581 | if (msg_level < 0 && ((end - cur_index) > 2)) { |
517 | LOG_BUF(cur_index + 0) == '<' && | 582 | /* strip log prefix */ |
518 | LOG_BUF(cur_index + 1) >= '0' && | 583 | cur_index += log_prefix(&LOG_BUF(cur_index), &msg_level, NULL); |
519 | LOG_BUF(cur_index + 1) <= '7' && | ||
520 | LOG_BUF(cur_index + 2) == '>') { | ||
521 | msg_level = LOG_BUF(cur_index + 1) - '0'; | ||
522 | cur_index += 3; | ||
523 | start_print = cur_index; | 584 | start_print = cur_index; |
524 | } | 585 | } |
525 | while (cur_index != end) { | 586 | while (cur_index != end) { |
@@ -717,6 +778,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) | |||
717 | unsigned long flags; | 778 | unsigned long flags; |
718 | int this_cpu; | 779 | int this_cpu; |
719 | char *p; | 780 | char *p; |
781 | size_t plen; | ||
782 | char special; | ||
720 | 783 | ||
721 | boot_delay_msec(); | 784 | boot_delay_msec(); |
722 | printk_delay(); | 785 | printk_delay(); |
@@ -757,45 +820,52 @@ asmlinkage int vprintk(const char *fmt, va_list args) | |||
757 | printed_len += vscnprintf(printk_buf + printed_len, | 820 | printed_len += vscnprintf(printk_buf + printed_len, |
758 | sizeof(printk_buf) - printed_len, fmt, args); | 821 | sizeof(printk_buf) - printed_len, fmt, args); |
759 | 822 | ||
760 | |||
761 | p = printk_buf; | 823 | p = printk_buf; |
762 | 824 | ||
763 | /* Do we have a loglevel in the string? */ | 825 | /* Read log level and handle special printk prefix */ |
764 | if (p[0] == '<') { | 826 | plen = log_prefix(p, ¤t_log_level, &special); |
765 | unsigned char c = p[1]; | 827 | if (plen) { |
766 | if (c && p[2] == '>') { | 828 | p += plen; |
767 | switch (c) { | 829 | |
768 | case '0' ... '7': /* loglevel */ | 830 | switch (special) { |
769 | current_log_level = c - '0'; | 831 | case 'c': /* Strip <c> KERN_CONT, continue line */ |
770 | /* Fallthrough - make sure we're on a new line */ | 832 | plen = 0; |
771 | case 'd': /* KERN_DEFAULT */ | 833 | break; |
772 | if (!new_text_line) { | 834 | case 'd': /* Strip <d> KERN_DEFAULT, start new line */ |
773 | emit_log_char('\n'); | 835 | plen = 0; |
774 | new_text_line = 1; | 836 | default: |
775 | } | 837 | if (!new_text_line) { |
776 | /* Fallthrough - skip the loglevel */ | 838 | emit_log_char('\n'); |
777 | case 'c': /* KERN_CONT */ | 839 | new_text_line = 1; |
778 | p += 3; | ||
779 | break; | ||
780 | } | 840 | } |
781 | } | 841 | } |
782 | } | 842 | } |
783 | 843 | ||
784 | /* | 844 | /* |
785 | * Copy the output into log_buf. If the caller didn't provide | 845 | * Copy the output into log_buf. If the caller didn't provide |
786 | * appropriate log level tags, we insert them here | 846 | * the appropriate log prefix, we insert them here |
787 | */ | 847 | */ |
788 | for ( ; *p; p++) { | 848 | for (; *p; p++) { |
789 | if (new_text_line) { | 849 | if (new_text_line) { |
790 | /* Always output the token */ | ||
791 | emit_log_char('<'); | ||
792 | emit_log_char(current_log_level + '0'); | ||
793 | emit_log_char('>'); | ||
794 | printed_len += 3; | ||
795 | new_text_line = 0; | 850 | new_text_line = 0; |
796 | 851 | ||
852 | if (plen) { | ||
853 | /* Copy original log prefix */ | ||
854 | int i; | ||
855 | |||
856 | for (i = 0; i < plen; i++) | ||
857 | emit_log_char(printk_buf[i]); | ||
858 | printed_len += plen; | ||
859 | } else { | ||
860 | /* Add log prefix */ | ||
861 | emit_log_char('<'); | ||
862 | emit_log_char(current_log_level + '0'); | ||
863 | emit_log_char('>'); | ||
864 | printed_len += 3; | ||
865 | } | ||
866 | |||
797 | if (printk_time) { | 867 | if (printk_time) { |
798 | /* Follow the token with the time */ | 868 | /* Add the current time stamp */ |
799 | char tbuf[50], *tp; | 869 | char tbuf[50], *tp; |
800 | unsigned tlen; | 870 | unsigned tlen; |
801 | unsigned long long t; | 871 | unsigned long long t; |