Commit a7605628 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'kgdb-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux

Pull kgdb updates from Daniel Thompson:
 "Nine patches this cycle and they split into just three topics:

   - Adopt coccinelle's recommendation to adopt str_plural()

   - A set of seven patches to refactor kdb_read() to improve both code
     clarity and its discipline with respect to fixed size buffers.

     This isn't just a refactor. Between them these also fix a cursor
     movement redraw problem and two buffer overflows (one latent and
     one real, albeit difficult to tickle).

   - Fix an NMI-safety problem when enqueuing kdb's keyboard reset code

  I wrote eight of the nine patches in this collection so many thanks to
  Doug Anderson for the reviews. The changes that affects
  drivers/tty/serial is acked by Greg KH"

* tag 'kgdb-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux:
  serial: kgdboc: Fix NMI-safety problems from keyboard reset code
  kdb: Simplify management of tmpbuffer in kdb_read()
  kdb: Replace double memcpy() with memmove() in kdb_read()
  kdb: Use format-specifiers rather than memset() for padding in kdb_read()
  kdb: Merge identical case statements in kdb_read()
  kdb: Fix console handling when editing and tab-completing commands
  kdb: Use format-strings rather than '\0' injection in kdb_read()
  kdb: Fix buffer overflow during tab-complete
  kdb: Use str_plural() to fix Coccinelle warning
parents 41c14f1a b2aba15a
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/console.h> #include <linux/console.h>
#include <linux/vt_kern.h> #include <linux/vt_kern.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/irq_work.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
...@@ -48,6 +49,25 @@ static struct kgdb_io kgdboc_earlycon_io_ops; ...@@ -48,6 +49,25 @@ static struct kgdb_io kgdboc_earlycon_io_ops;
static int (*earlycon_orig_exit)(struct console *con); static int (*earlycon_orig_exit)(struct console *con);
#endif /* IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */ #endif /* IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */
/*
* When we leave the debug trap handler we need to reset the keyboard status
* (since the original keyboard state gets partially clobbered by kdb use of
* the keyboard).
*
* The path to deliver the reset is somewhat circuitous.
*
* To deliver the reset we register an input handler, reset the keyboard and
* then deregister the input handler. However, to get this done right, we do
* have to carefully manage the calling context because we can only register
* input handlers from task context.
*
* In particular we need to trigger the action from the debug trap handler with
* all its NMI and/or NMI-like oddities. To solve this the kgdboc trap exit code
* (the "post_exception" callback) uses irq_work_queue(), which is NMI-safe, to
* schedule a callback from a hardirq context. From there we have to defer the
* work again, this time using schedule_work(), to get a callback using the
* system workqueue, which runs in task context.
*/
#ifdef CONFIG_KDB_KEYBOARD #ifdef CONFIG_KDB_KEYBOARD
static int kgdboc_reset_connect(struct input_handler *handler, static int kgdboc_reset_connect(struct input_handler *handler,
struct input_dev *dev, struct input_dev *dev,
...@@ -99,10 +119,17 @@ static void kgdboc_restore_input_helper(struct work_struct *dummy) ...@@ -99,10 +119,17 @@ static void kgdboc_restore_input_helper(struct work_struct *dummy)
static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper);
static void kgdboc_queue_restore_input_helper(struct irq_work *unused)
{
schedule_work(&kgdboc_restore_input_work);
}
static DEFINE_IRQ_WORK(kgdboc_restore_input_irq_work, kgdboc_queue_restore_input_helper);
static void kgdboc_restore_input(void) static void kgdboc_restore_input(void)
{ {
if (likely(system_state == SYSTEM_RUNNING)) if (likely(system_state == SYSTEM_RUNNING))
schedule_work(&kgdboc_restore_input_work); irq_work_queue(&kgdboc_restore_input_irq_work);
} }
static int kgdboc_register_kbd(char **cptr) static int kgdboc_register_kbd(char **cptr)
...@@ -133,6 +160,7 @@ static void kgdboc_unregister_kbd(void) ...@@ -133,6 +160,7 @@ static void kgdboc_unregister_kbd(void)
i--; i--;
} }
} }
irq_work_sync(&kgdboc_restore_input_irq_work);
flush_work(&kgdboc_restore_input_work); flush_work(&kgdboc_restore_input_work);
} }
#else /* ! CONFIG_KDB_KEYBOARD */ #else /* ! CONFIG_KDB_KEYBOARD */
......
...@@ -184,6 +184,33 @@ char kdb_getchar(void) ...@@ -184,6 +184,33 @@ char kdb_getchar(void)
unreachable(); unreachable();
} }
/**
* kdb_position_cursor() - Place cursor in the correct horizontal position
* @prompt: Nil-terminated string containing the prompt string
* @buffer: Nil-terminated string containing the entire command line
* @cp: Cursor position, pointer the character in buffer where the cursor
* should be positioned.
*
* The cursor is positioned by sending a carriage-return and then printing
* the content of the line until we reach the correct cursor position.
*
* There is some additional fine detail here.
*
* Firstly, even though kdb_printf() will correctly format zero-width fields
* we want the second call to kdb_printf() to be conditional. That keeps things
* a little cleaner when LOGGING=1.
*
* Secondly, we can't combine everything into one call to kdb_printf() since
* that renders into a fixed length buffer and the combined print could result
* in unwanted truncation.
*/
static void kdb_position_cursor(char *prompt, char *buffer, char *cp)
{
kdb_printf("\r%s", kdb_prompt_str);
if (cp > buffer)
kdb_printf("%.*s", (int)(cp - buffer), buffer);
}
/* /*
* kdb_read * kdb_read
* *
...@@ -220,8 +247,7 @@ static char *kdb_read(char *buffer, size_t bufsize) ...@@ -220,8 +247,7 @@ static char *kdb_read(char *buffer, size_t bufsize)
int count; int count;
int i; int i;
int diag, dtab_count; int diag, dtab_count;
int key, buf_size, ret; int key, ret;
diag = kdbgetintenv("DTABCOUNT", &dtab_count); diag = kdbgetintenv("DTABCOUNT", &dtab_count);
if (diag) if (diag)
...@@ -243,18 +269,11 @@ static char *kdb_read(char *buffer, size_t bufsize) ...@@ -243,18 +269,11 @@ static char *kdb_read(char *buffer, size_t bufsize)
switch (key) { switch (key) {
case 8: /* backspace */ case 8: /* backspace */
if (cp > buffer) { if (cp > buffer) {
if (cp < lastchar) { memmove(cp-1, cp, lastchar - cp + 1);
memcpy(tmpbuffer, cp, lastchar - cp); lastchar--;
memcpy(cp-1, tmpbuffer, lastchar - cp); cp--;
} kdb_printf("\b%s ", cp);
*(--lastchar) = '\0'; kdb_position_cursor(kdb_prompt_str, buffer, cp);
--cp;
kdb_printf("\b%s \r", cp);
tmp = *cp;
*cp = '\0';
kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer);
*cp = tmp;
} }
break; break;
case 10: /* linefeed */ case 10: /* linefeed */
...@@ -269,22 +288,16 @@ static char *kdb_read(char *buffer, size_t bufsize) ...@@ -269,22 +288,16 @@ static char *kdb_read(char *buffer, size_t bufsize)
return buffer; return buffer;
case 4: /* Del */ case 4: /* Del */
if (cp < lastchar) { if (cp < lastchar) {
memcpy(tmpbuffer, cp+1, lastchar - cp - 1); memmove(cp, cp+1, lastchar - cp);
memcpy(cp, tmpbuffer, lastchar - cp - 1); lastchar--;
*(--lastchar) = '\0'; kdb_printf("%s ", cp);
kdb_printf("%s \r", cp); kdb_position_cursor(kdb_prompt_str, buffer, cp);
tmp = *cp;
*cp = '\0';
kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer);
*cp = tmp;
} }
break; break;
case 1: /* Home */ case 1: /* Home */
if (cp > buffer) { if (cp > buffer) {
kdb_printf("\r");
kdb_printf(kdb_prompt_str);
cp = buffer; cp = buffer;
kdb_position_cursor(kdb_prompt_str, buffer, cp);
} }
break; break;
case 5: /* End */ case 5: /* End */
...@@ -300,11 +313,10 @@ static char *kdb_read(char *buffer, size_t bufsize) ...@@ -300,11 +313,10 @@ static char *kdb_read(char *buffer, size_t bufsize)
} }
break; break;
case 14: /* Down */ case 14: /* Down */
memset(tmpbuffer, ' ', case 16: /* Up */
strlen(kdb_prompt_str) + (lastchar-buffer)); kdb_printf("\r%*c\r",
*(tmpbuffer+strlen(kdb_prompt_str) + (int)(strlen(kdb_prompt_str) + (lastchar - buffer)),
(lastchar-buffer)) = '\0'; ' ');
kdb_printf("\r%s\r", tmpbuffer);
*lastchar = (char)key; *lastchar = (char)key;
*(lastchar+1) = '\0'; *(lastchar+1) = '\0';
return lastchar; return lastchar;
...@@ -314,33 +326,19 @@ static char *kdb_read(char *buffer, size_t bufsize) ...@@ -314,33 +326,19 @@ static char *kdb_read(char *buffer, size_t bufsize)
++cp; ++cp;
} }
break; break;
case 16: /* Up */
memset(tmpbuffer, ' ',
strlen(kdb_prompt_str) + (lastchar-buffer));
*(tmpbuffer+strlen(kdb_prompt_str) +
(lastchar-buffer)) = '\0';
kdb_printf("\r%s\r", tmpbuffer);
*lastchar = (char)key;
*(lastchar+1) = '\0';
return lastchar;
case 9: /* Tab */ case 9: /* Tab */
if (tab < 2) if (tab < 2)
++tab; ++tab;
p_tmp = buffer;
while (*p_tmp == ' ') tmp = *cp;
p_tmp++; *cp = '\0';
if (p_tmp > cp) p_tmp = strrchr(buffer, ' ');
break; p_tmp = (p_tmp ? p_tmp + 1 : buffer);
memcpy(tmpbuffer, p_tmp, cp-p_tmp); strscpy(tmpbuffer, p_tmp, sizeof(tmpbuffer));
*(tmpbuffer + (cp-p_tmp)) = '\0'; *cp = tmp;
p_tmp = strrchr(tmpbuffer, ' ');
if (p_tmp) len = strlen(tmpbuffer);
++p_tmp; count = kallsyms_symbol_complete(tmpbuffer, sizeof(tmpbuffer));
else
p_tmp = tmpbuffer;
len = strlen(p_tmp);
buf_size = sizeof(tmpbuffer) - (p_tmp - tmpbuffer);
count = kallsyms_symbol_complete(p_tmp, buf_size);
if (tab == 2 && count > 0) { if (tab == 2 && count > 0) {
kdb_printf("\n%d symbols are found.", count); kdb_printf("\n%d symbols are found.", count);
if (count > dtab_count) { if (count > dtab_count) {
...@@ -352,46 +350,51 @@ static char *kdb_read(char *buffer, size_t bufsize) ...@@ -352,46 +350,51 @@ static char *kdb_read(char *buffer, size_t bufsize)
} }
kdb_printf("\n"); kdb_printf("\n");
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
ret = kallsyms_symbol_next(p_tmp, i, buf_size); ret = kallsyms_symbol_next(tmpbuffer, i, sizeof(tmpbuffer));
if (WARN_ON(!ret)) if (WARN_ON(!ret))
break; break;
if (ret != -E2BIG) if (ret != -E2BIG)
kdb_printf("%s ", p_tmp); kdb_printf("%s ", tmpbuffer);
else else
kdb_printf("%s... ", p_tmp); kdb_printf("%s... ", tmpbuffer);
*(p_tmp + len) = '\0'; tmpbuffer[len] = '\0';
} }
if (i >= dtab_count) if (i >= dtab_count)
kdb_printf("..."); kdb_printf("...");
kdb_printf("\n"); kdb_printf("\n");
kdb_printf(kdb_prompt_str); kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer); kdb_printf("%s", buffer);
if (cp != lastchar)
kdb_position_cursor(kdb_prompt_str, buffer, cp);
} else if (tab != 2 && count > 0) { } else if (tab != 2 && count > 0) {
len_tmp = strlen(p_tmp); /* How many new characters do we want from tmpbuffer? */
strncpy(p_tmp+len_tmp, cp, lastchar-cp+1); len_tmp = strlen(tmpbuffer) - len;
len_tmp = strlen(p_tmp); if (lastchar + len_tmp >= bufend)
strncpy(cp, p_tmp+len, len_tmp-len + 1); len_tmp = bufend - lastchar;
len = len_tmp - len;
kdb_printf("%s", cp); if (len_tmp) {
cp += len; /* + 1 ensures the '\0' is memmove'd */
lastchar += len; memmove(cp+len_tmp, cp, (lastchar-cp) + 1);
memcpy(cp, tmpbuffer+len, len_tmp);
kdb_printf("%s", cp);
cp += len_tmp;
lastchar += len_tmp;
if (cp != lastchar)
kdb_position_cursor(kdb_prompt_str,
buffer, cp);
}
} }
kdb_nextline = 1; /* reset output line number */ kdb_nextline = 1; /* reset output line number */
break; break;
default: default:
if (key >= 32 && lastchar < bufend) { if (key >= 32 && lastchar < bufend) {
if (cp < lastchar) { if (cp < lastchar) {
memcpy(tmpbuffer, cp, lastchar - cp); memmove(cp+1, cp, lastchar - cp + 1);
memcpy(cp+1, tmpbuffer, lastchar - cp); lastchar++;
*++lastchar = '\0';
*cp = key; *cp = key;
kdb_printf("%s\r", cp); kdb_printf("%s", cp);
++cp; ++cp;
tmp = *cp; kdb_position_cursor(kdb_prompt_str, buffer, cp);
*cp = '\0';
kdb_printf(kdb_prompt_str);
kdb_printf("%s", buffer);
*cp = tmp;
} else { } else {
*++lastchar = '\0'; *++lastchar = '\0';
*cp++ = key; *cp++ = key;
......
...@@ -2517,7 +2517,7 @@ static int kdb_summary(int argc, const char **argv) ...@@ -2517,7 +2517,7 @@ static int kdb_summary(int argc, const char **argv)
if (val.uptime > (24*60*60)) { if (val.uptime > (24*60*60)) {
int days = val.uptime / (24*60*60); int days = val.uptime / (24*60*60);
val.uptime %= (24*60*60); val.uptime %= (24*60*60);
kdb_printf("%d day%s ", days, days == 1 ? "" : "s"); kdb_printf("%d day%s ", days, str_plural(days));
} }
kdb_printf("%02ld:%02ld\n", val.uptime/(60*60), (val.uptime/60)%60); kdb_printf("%02ld:%02ld\n", val.uptime/(60*60), (val.uptime/60)%60);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment