lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20200819232632.13418-2-john.ogness@linutronix.de>
Date:   Thu, 20 Aug 2020 01:32:28 +0206
From:   John Ogness <john.ogness@...utronix.de>
To:     Linus Torvalds <torvalds@...ux-foundation.org>
Cc:     Petr Mladek <pmladek@...e.com>,
        Sergey Senozhatsky <sergey.senozhatsky.work@...il.com>,
        Steven Rostedt <rostedt@...dmis.org>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Thomas Gleixner <tglx@...utronix.de>,
        Sergey Senozhatsky <sergey.senozhatsky@...il.com>,
        linux-kernel@...r.kernel.org
Subject: [RFC PATCH 1/5] printk: implement pr_cont_t

Implement a new buffering mechanism for pr_cont messages.

Old mechanism syntax:

    printk(KERN_INFO "text");
    printk(KERN_CONT " continued");
    printk(KERN_CONT "\n");

New mechanism syntax:

    pr_cont_t c;

    pr_cont_begin(&c, KERN_INFO "text");
    pr_cont_append(&c, " continued");
    pr_cont_end(&c);

Signed-off-by: John Ogness <john.ogness@...utronix.de>
---
 include/linux/printk.h |  19 ++++++
 kernel/printk/printk.c | 137 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 156 insertions(+)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index 34c1a7be3e01..4d9ce18e4afa 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -380,6 +380,25 @@ extern int kptr_restrict;
 #define pr_cont(fmt, ...) \
 	printk(KERN_CONT fmt, ##__VA_ARGS__)
 
+/* opaque handle for continuous printk messages */
+typedef struct {
+	u8	index;
+	u8	loglevel;
+	u16	text_len;
+} pr_cont_t;
+
+/* initialize handle, provide loglevel and initial message text */
+int pr_cont_begin(pr_cont_t *c, const char *fmt, ...);
+
+/* append message text */
+int pr_cont_append(pr_cont_t *c, const char *fmt, ...);
+
+/* flush message to kernel buffer */
+void pr_cont_flush(pr_cont_t *c);
+
+/* flush message to kernel buffer, cleanup handle */
+void pr_cont_end(pr_cont_t *c);
+
 /**
  * pr_devel - Print a debug-level message conditionally
  * @fmt: format string
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index ad8d1dfe5fbe..10113e7ea350 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -3495,3 +3495,140 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper)
 EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
 
 #endif
+
+#define CONT_LINE_MAX LOG_LINE_MAX
+#define CONT_BUF_COUNT 10
+static char cont_buf[CONT_BUF_COUNT][CONT_LINE_MAX];
+static DECLARE_BITMAP(cont_buf_map, CONT_BUF_COUNT);
+
+static int get_cont_buf(void)
+{
+	int bit;
+
+	do {
+		bit = find_first_zero_bit(cont_buf_map, CONT_BUF_COUNT);
+		if (bit == CONT_BUF_COUNT)
+			break;
+	} while (test_and_set_bit(bit, cont_buf_map));
+
+	return bit;
+}
+
+static void put_cont_buf(int index)
+{
+	if (WARN_ON(index >= CONT_BUF_COUNT))
+		return;
+	if (WARN_ON(!test_bit(index, cont_buf_map)))
+		return;
+	clear_bit(index, cont_buf_map);
+}
+
+/* alloc buffer, get loglevel, setup initial text */
+int pr_cont_begin(pr_cont_t *c, const char *fmt, ...)
+{
+	int kern_level;
+	va_list args;
+	char *text;
+
+	c->index = get_cont_buf();
+	if (c->index == CONT_BUF_COUNT) {
+		/* Fallback to printk parts individually. */
+		int text_len;
+
+		va_start(args, fmt);
+		text_len = vprintk(fmt, args);
+		va_end(args);
+		return text_len;
+	}
+
+	text = &cont_buf[c->index][0];
+
+	va_start(args, fmt);
+	c->text_len = vscnprintf(text, CONT_LINE_MAX, fmt, args);
+	va_end(args);
+
+	c->loglevel = default_message_loglevel;
+	while (c->text_len > 1 &&
+	       (kern_level = printk_get_level(text)) != 0) {
+		switch (kern_level) {
+		case '0' ... '7':
+			c->loglevel = kern_level - '0';
+			break;
+		}
+
+		c->text_len -= 2;
+		memmove(text, text + 2, c->text_len);
+	}
+
+	return c->text_len;
+}
+
+/* printk existing buffer, reset buffer */
+void pr_cont_flush(pr_cont_t *c)
+{
+	char *text;
+
+	if (c->index == CONT_BUF_COUNT || !c->text_len)
+		return;
+
+	text = &cont_buf[c->index][0];
+
+	printk("%c%c%s\n", KERN_SOH_ASCII, c->loglevel + '0', text);
+
+	c->text_len = 0;
+}
+
+/* printk existing buffer, free buffer */
+void pr_cont_end(pr_cont_t *c)
+{
+	if (c->index == CONT_BUF_COUNT)
+		return;
+
+	pr_cont_flush(c);
+	put_cont_buf(c->index);
+	c->index = CONT_BUF_COUNT;
+}
+
+/* append to buffer */
+int pr_cont_append(pr_cont_t *c, const char *fmt, ...)
+{
+	va_list args_copy;
+	va_list args;
+	int text_len;
+	char *text;
+
+	if (c->index == CONT_BUF_COUNT) {
+		/* Fallback to printk parts individually. */
+		va_start(args, fmt);
+		text_len = vprintk(fmt, args);
+		va_end(args);
+		return text_len;
+	}
+
+	text = &cont_buf[c->index][0];
+
+	/* Try to append directly. */
+	va_start(args, fmt);
+	va_copy(args_copy, args);
+	text_len = vsnprintf(text + c->text_len, CONT_LINE_MAX - c->text_len, fmt, args);
+	va_end(args);
+
+	if (text_len >= CONT_LINE_MAX - c->text_len) {
+		/*
+		 * Not enough space remaining. Set the overwritten terminator,
+		 * Flush any existing parts and start with a clean buffer.
+		 * The loglevel is preserved.
+		 */
+
+		text[c->text_len] = 0;
+		pr_cont_flush(c);
+
+		va_start(args_copy, fmt);
+		c->text_len = vscnprintf(text, CONT_LINE_MAX, fmt, args_copy);
+		va_end(args_copy);
+		return c->text_len;
+	}
+
+	c->text_len += text_len;
+	return text_len;
+}
-- 
2.20.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ