Kyle McMartin 1d3db7f
From: Kay Sievers <kay.sievers@vrfy.org>
Kyle McMartin 1d3db7f
Date: Sun, 13 Mar 2011 02:19:51 +0000 (+0100)
Kyle McMartin 1d3db7f
Subject: printk: do not mangle valid userspace syslog prefixes
Kyle McMartin 1d3db7f
X-Git-Tag: v2.6.39-rc1~471^2
Kyle McMartin 1d3db7f
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=9d90c8d9cde929cbc575098e825d7c29d9f45054
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
printk: do not mangle valid userspace syslog prefixes
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
printk: do not mangle valid userspace syslog prefixes with /dev/kmsg
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
Log messages passed to the kernel log by using /dev/kmsg or /dev/ttyprintk
Kyle McMartin 1d3db7f
might contain a syslog prefix including the syslog facility value.
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
This makes printk to recognize these headers properly, extract the real log
Kyle McMartin 1d3db7f
level from it to use, and add the prefix as a proper prefix to the
Kyle McMartin 1d3db7f
log buffer, instead of wrongly printing it as the log message text.
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
Before:
Kyle McMartin 1d3db7f
  $ echo '<14>text' > /dev/kmsg
Kyle McMartin 1d3db7f
  $ dmesg -r
Kyle McMartin 1d3db7f
  <4>[135159.594810] <14>text
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
After:
Kyle McMartin 1d3db7f
  $ echo '<14>text' > /dev/kmsg
Kyle McMartin 1d3db7f
  $ dmesg -r
Kyle McMartin 1d3db7f
  <14>[   50.750654] text
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
Cc: Lennart Poettering <lennart@poettering.net>
Kyle McMartin 1d3db7f
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Kyle McMartin 1d3db7f
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Kyle McMartin 1d3db7f
---
Kyle McMartin 1d3db7f
Kyle McMartin 1d3db7f
diff --git a/kernel/printk.c b/kernel/printk.c
Kyle McMartin 1d3db7f
index 2ddbdc7..5e3d042 100644
Kyle McMartin 1d3db7f
--- a/kernel/printk.c
Kyle McMartin 1d3db7f
+++ b/kernel/printk.c
Kyle McMartin 1d3db7f
@@ -499,6 +499,71 @@ static void _call_console_drivers(unsigned start,
Kyle McMartin 1d3db7f
 }
Kyle McMartin 1d3db7f
 
Kyle McMartin 1d3db7f
 /*
Kyle McMartin 1d3db7f
+ * Parse the syslog header <[0-9]*>. The decimal value represents 32bit, the
Kyle McMartin 1d3db7f
+ * lower 3 bit are the log level, the rest are the log facility. In case
Kyle McMartin 1d3db7f
+ * userspace passes usual userspace syslog messages to /dev/kmsg or
Kyle McMartin 1d3db7f
+ * /dev/ttyprintk, the log prefix might contain the facility. Printk needs
Kyle McMartin 1d3db7f
+ * to extract the correct log level for in-kernel processing, and not mangle
Kyle McMartin 1d3db7f
+ * the original value.
Kyle McMartin 1d3db7f
+ *
Kyle McMartin 1d3db7f
+ * If a prefix is found, the length of the prefix is returned. If 'level' is
Kyle McMartin 1d3db7f
+ * passed, it will be filled in with the log level without a possible facility
Kyle McMartin 1d3db7f
+ * value. If 'special' is passed, the special printk prefix chars are accepted
Kyle McMartin 1d3db7f
+ * and returned. If no valid header is found, 0 is returned and the passed
Kyle McMartin 1d3db7f
+ * variables are not touched.
Kyle McMartin 1d3db7f
+ */
Kyle McMartin 1d3db7f
+static size_t log_prefix(const char *p, unsigned int *level, char *special)
Kyle McMartin 1d3db7f
+{
Kyle McMartin 1d3db7f
+	unsigned int lev = 0;
Kyle McMartin 1d3db7f
+	char sp = '\0';
Kyle McMartin 1d3db7f
+	size_t len;
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+	if (p[0] != '<' || !p[1])
Kyle McMartin 1d3db7f
+		return 0;
Kyle McMartin 1d3db7f
+	if (p[2] == '>') {
Kyle McMartin 1d3db7f
+		/* usual single digit level number or special char */
Kyle McMartin 1d3db7f
+		switch (p[1]) {
Kyle McMartin 1d3db7f
+		case '0' ... '7':
Kyle McMartin 1d3db7f
+			lev = p[1] - '0';
Kyle McMartin 1d3db7f
+			break;
Kyle McMartin 1d3db7f
+		case 'c': /* KERN_CONT */
Kyle McMartin 1d3db7f
+		case 'd': /* KERN_DEFAULT */
Kyle McMartin 1d3db7f
+			sp = p[1];
Kyle McMartin 1d3db7f
+			break;
Kyle McMartin 1d3db7f
+		default:
Kyle McMartin 1d3db7f
+			return 0;
Kyle McMartin 1d3db7f
+		}
Kyle McMartin 1d3db7f
+		len = 3;
Kyle McMartin 1d3db7f
+	} else {
Kyle McMartin 1d3db7f
+		/* multi digit including the level and facility number */
Kyle McMartin 1d3db7f
+		char *endp = NULL;
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+		if (p[1] < '0' && p[1] > '9')
Kyle McMartin 1d3db7f
+			return 0;
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+		lev = (simple_strtoul(&p[1], &endp, 10) & 7);
Kyle McMartin 1d3db7f
+		if (endp == NULL || endp[0] != '>')
Kyle McMartin 1d3db7f
+			return 0;
Kyle McMartin 1d3db7f
+		len = (endp + 1) - p;
Kyle McMartin 1d3db7f
+	}
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+	/* do not accept special char if not asked for */
Kyle McMartin 1d3db7f
+	if (sp && !special)
Kyle McMartin 1d3db7f
+		return 0;
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+	if (special) {
Kyle McMartin 1d3db7f
+		*special = sp;
Kyle McMartin 1d3db7f
+		/* return special char, do not touch level */
Kyle McMartin 1d3db7f
+		if (sp)
Kyle McMartin 1d3db7f
+			return len;
Kyle McMartin 1d3db7f
+	}
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+	if (level)
Kyle McMartin 1d3db7f
+		*level = lev;
Kyle McMartin 1d3db7f
+	return len;
Kyle McMartin 1d3db7f
+}
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+/*
Kyle McMartin 1d3db7f
  * Call the console drivers, asking them to write out
Kyle McMartin 1d3db7f
  * log_buf[start] to log_buf[end - 1].
Kyle McMartin 1d3db7f
  * The console_lock must be held.
Kyle McMartin 1d3db7f
@@ -513,13 +578,9 @@ static void call_console_drivers(unsigned start, unsigned end)
Kyle McMartin 1d3db7f
 	cur_index = start;
Kyle McMartin 1d3db7f
 	start_print = start;
Kyle McMartin 1d3db7f
 	while (cur_index != end) {
Kyle McMartin 1d3db7f
-		if (msg_level < 0 && ((end - cur_index) > 2) &&
Kyle McMartin 1d3db7f
-				LOG_BUF(cur_index + 0) == '<' &&
Kyle McMartin 1d3db7f
-				LOG_BUF(cur_index + 1) >= '0' &&
Kyle McMartin 1d3db7f
-				LOG_BUF(cur_index + 1) <= '7' &&
Kyle McMartin 1d3db7f
-				LOG_BUF(cur_index + 2) == '>') {
Kyle McMartin 1d3db7f
-			msg_level = LOG_BUF(cur_index + 1) - '0';
Kyle McMartin 1d3db7f
-			cur_index += 3;
Kyle McMartin 1d3db7f
+		if (msg_level < 0 && ((end - cur_index) > 2)) {
Kyle McMartin 1d3db7f
+			/* strip log prefix */
Kyle McMartin 1d3db7f
+			cur_index += log_prefix(&LOG_BUF(cur_index), &msg_level, NULL);
Kyle McMartin 1d3db7f
 			start_print = cur_index;
Kyle McMartin 1d3db7f
 		}
Kyle McMartin 1d3db7f
 		while (cur_index != end) {
Kyle McMartin 1d3db7f
@@ -717,6 +778,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
Kyle McMartin 1d3db7f
 	unsigned long flags;
Kyle McMartin 1d3db7f
 	int this_cpu;
Kyle McMartin 1d3db7f
 	char *p;
Kyle McMartin 1d3db7f
+	size_t plen;
Kyle McMartin 1d3db7f
+	char special;
Kyle McMartin 1d3db7f
 
Kyle McMartin 1d3db7f
 	boot_delay_msec();
Kyle McMartin 1d3db7f
 	printk_delay();
Kyle McMartin 1d3db7f
@@ -757,45 +820,52 @@ asmlinkage int vprintk(const char *fmt, va_list args)
Kyle McMartin 1d3db7f
 	printed_len += vscnprintf(printk_buf + printed_len,
Kyle McMartin 1d3db7f
 				  sizeof(printk_buf) - printed_len, fmt, args);
Kyle McMartin 1d3db7f
 
Kyle McMartin 1d3db7f
-
Kyle McMartin 1d3db7f
 	p = printk_buf;
Kyle McMartin 1d3db7f
 
Kyle McMartin 1d3db7f
-	/* Do we have a loglevel in the string? */
Kyle McMartin 1d3db7f
-	if (p[0] == '<') {
Kyle McMartin 1d3db7f
-		unsigned char c = p[1];
Kyle McMartin 1d3db7f
-		if (c && p[2] == '>') {
Kyle McMartin 1d3db7f
-			switch (c) {
Kyle McMartin 1d3db7f
-			case '0' ... '7': /* loglevel */
Kyle McMartin 1d3db7f
-				current_log_level = c - '0';
Kyle McMartin 1d3db7f
-			/* Fallthrough - make sure we're on a new line */
Kyle McMartin 1d3db7f
-			case 'd': /* KERN_DEFAULT */
Kyle McMartin 1d3db7f
-				if (!new_text_line) {
Kyle McMartin 1d3db7f
-					emit_log_char('\n');
Kyle McMartin 1d3db7f
-					new_text_line = 1;
Kyle McMartin 1d3db7f
-				}
Kyle McMartin 1d3db7f
-			/* Fallthrough - skip the loglevel */
Kyle McMartin 1d3db7f
-			case 'c': /* KERN_CONT */
Kyle McMartin 1d3db7f
-				p += 3;
Kyle McMartin 1d3db7f
-				break;
Kyle McMartin 1d3db7f
+	/* Read log level and handle special printk prefix */
Kyle McMartin 1d3db7f
+	plen = log_prefix(p, &current_log_level, &special);
Kyle McMartin 1d3db7f
+	if (plen) {
Kyle McMartin 1d3db7f
+		p += plen;
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+		switch (special) {
Kyle McMartin 1d3db7f
+		case 'c': /* Strip <c> KERN_CONT, continue line */
Kyle McMartin 1d3db7f
+			plen = 0;
Kyle McMartin 1d3db7f
+			break;
Kyle McMartin 1d3db7f
+		case 'd': /* Strip <d> KERN_DEFAULT, start new line */
Kyle McMartin 1d3db7f
+			plen = 0;
Kyle McMartin 1d3db7f
+		default:
Kyle McMartin 1d3db7f
+			if (!new_text_line) {
Kyle McMartin 1d3db7f
+				emit_log_char('\n');
Kyle McMartin 1d3db7f
+				new_text_line = 1;
Kyle McMartin 1d3db7f
 			}
Kyle McMartin 1d3db7f
 		}
Kyle McMartin 1d3db7f
 	}
Kyle McMartin 1d3db7f
 
Kyle McMartin 1d3db7f
 	/*
Kyle McMartin 1d3db7f
-	 * Copy the output into log_buf.  If the caller didn't provide
Kyle McMartin 1d3db7f
-	 * appropriate log level tags, we insert them here
Kyle McMartin 1d3db7f
+	 * Copy the output into log_buf. If the caller didn't provide
Kyle McMartin 1d3db7f
+	 * the appropriate log prefix, we insert them here
Kyle McMartin 1d3db7f
 	 */
Kyle McMartin 1d3db7f
-	for ( ; *p; p++) {
Kyle McMartin 1d3db7f
+	for (; *p; p++) {
Kyle McMartin 1d3db7f
 		if (new_text_line) {
Kyle McMartin 1d3db7f
-			/* Always output the token */
Kyle McMartin 1d3db7f
-			emit_log_char('<');
Kyle McMartin 1d3db7f
-			emit_log_char(current_log_level + '0');
Kyle McMartin 1d3db7f
-			emit_log_char('>');
Kyle McMartin 1d3db7f
-			printed_len += 3;
Kyle McMartin 1d3db7f
 			new_text_line = 0;
Kyle McMartin 1d3db7f
 
Kyle McMartin 1d3db7f
+			if (plen) {
Kyle McMartin 1d3db7f
+				/* Copy original log prefix */
Kyle McMartin 1d3db7f
+				int i;
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
+				for (i = 0; i < plen; i++)
Kyle McMartin 1d3db7f
+					emit_log_char(printk_buf[i]);
Kyle McMartin 1d3db7f
+				printed_len += plen;
Kyle McMartin 1d3db7f
+			} else {
Kyle McMartin 1d3db7f
+				/* Add log prefix */
Kyle McMartin 1d3db7f
+				emit_log_char('<');
Kyle McMartin 1d3db7f
+				emit_log_char(current_log_level + '0');
Kyle McMartin 1d3db7f
+				emit_log_char('>');
Kyle McMartin 1d3db7f
+				printed_len += 3;
Kyle McMartin 1d3db7f
+			}
Kyle McMartin 1d3db7f
+
Kyle McMartin 1d3db7f
 			if (printk_time) {
Kyle McMartin 1d3db7f
-				/* Follow the token with the time */
Kyle McMartin 1d3db7f
+				/* Add the current time stamp */
Kyle McMartin 1d3db7f
 				char tbuf[50], *tp;
Kyle McMartin 1d3db7f
 				unsigned tlen;
Kyle McMartin 1d3db7f
 				unsigned long long t;