Skip to content

Commit a760562

Browse files
committed
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
2 parents 41c14f1 + b2aba15 commit a760562

File tree

3 files changed

+108
-77
lines changed

3 files changed

+108
-77
lines changed

drivers/tty/serial/kgdboc.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/console.h>
2020
#include <linux/vt_kern.h>
2121
#include <linux/input.h>
22+
#include <linux/irq_work.h>
2223
#include <linux/module.h>
2324
#include <linux/platform_device.h>
2425
#include <linux/serial_core.h>
@@ -48,6 +49,25 @@ static struct kgdb_io kgdboc_earlycon_io_ops;
4849
static int (*earlycon_orig_exit)(struct console *con);
4950
#endif /* IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */
5051

52+
/*
53+
* When we leave the debug trap handler we need to reset the keyboard status
54+
* (since the original keyboard state gets partially clobbered by kdb use of
55+
* the keyboard).
56+
*
57+
* The path to deliver the reset is somewhat circuitous.
58+
*
59+
* To deliver the reset we register an input handler, reset the keyboard and
60+
* then deregister the input handler. However, to get this done right, we do
61+
* have to carefully manage the calling context because we can only register
62+
* input handlers from task context.
63+
*
64+
* In particular we need to trigger the action from the debug trap handler with
65+
* all its NMI and/or NMI-like oddities. To solve this the kgdboc trap exit code
66+
* (the "post_exception" callback) uses irq_work_queue(), which is NMI-safe, to
67+
* schedule a callback from a hardirq context. From there we have to defer the
68+
* work again, this time using schedule_work(), to get a callback using the
69+
* system workqueue, which runs in task context.
70+
*/
5171
#ifdef CONFIG_KDB_KEYBOARD
5272
static int kgdboc_reset_connect(struct input_handler *handler,
5373
struct input_dev *dev,
@@ -99,10 +119,17 @@ static void kgdboc_restore_input_helper(struct work_struct *dummy)
99119

100120
static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper);
101121

122+
static void kgdboc_queue_restore_input_helper(struct irq_work *unused)
123+
{
124+
schedule_work(&kgdboc_restore_input_work);
125+
}
126+
127+
static DEFINE_IRQ_WORK(kgdboc_restore_input_irq_work, kgdboc_queue_restore_input_helper);
128+
102129
static void kgdboc_restore_input(void)
103130
{
104131
if (likely(system_state == SYSTEM_RUNNING))
105-
schedule_work(&kgdboc_restore_input_work);
132+
irq_work_queue(&kgdboc_restore_input_irq_work);
106133
}
107134

108135
static int kgdboc_register_kbd(char **cptr)
@@ -133,6 +160,7 @@ static void kgdboc_unregister_kbd(void)
133160
i--;
134161
}
135162
}
163+
irq_work_sync(&kgdboc_restore_input_irq_work);
136164
flush_work(&kgdboc_restore_input_work);
137165
}
138166
#else /* ! CONFIG_KDB_KEYBOARD */

kernel/debug/kdb/kdb_io.c

Lines changed: 78 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,33 @@ char kdb_getchar(void)
184184
unreachable();
185185
}
186186

187+
/**
188+
* kdb_position_cursor() - Place cursor in the correct horizontal position
189+
* @prompt: Nil-terminated string containing the prompt string
190+
* @buffer: Nil-terminated string containing the entire command line
191+
* @cp: Cursor position, pointer the character in buffer where the cursor
192+
* should be positioned.
193+
*
194+
* The cursor is positioned by sending a carriage-return and then printing
195+
* the content of the line until we reach the correct cursor position.
196+
*
197+
* There is some additional fine detail here.
198+
*
199+
* Firstly, even though kdb_printf() will correctly format zero-width fields
200+
* we want the second call to kdb_printf() to be conditional. That keeps things
201+
* a little cleaner when LOGGING=1.
202+
*
203+
* Secondly, we can't combine everything into one call to kdb_printf() since
204+
* that renders into a fixed length buffer and the combined print could result
205+
* in unwanted truncation.
206+
*/
207+
static void kdb_position_cursor(char *prompt, char *buffer, char *cp)
208+
{
209+
kdb_printf("\r%s", kdb_prompt_str);
210+
if (cp > buffer)
211+
kdb_printf("%.*s", (int)(cp - buffer), buffer);
212+
}
213+
187214
/*
188215
* kdb_read
189216
*
@@ -220,8 +247,7 @@ static char *kdb_read(char *buffer, size_t bufsize)
220247
int count;
221248
int i;
222249
int diag, dtab_count;
223-
int key, buf_size, ret;
224-
250+
int key, ret;
225251

226252
diag = kdbgetintenv("DTABCOUNT", &dtab_count);
227253
if (diag)
@@ -243,18 +269,11 @@ static char *kdb_read(char *buffer, size_t bufsize)
243269
switch (key) {
244270
case 8: /* backspace */
245271
if (cp > buffer) {
246-
if (cp < lastchar) {
247-
memcpy(tmpbuffer, cp, lastchar - cp);
248-
memcpy(cp-1, tmpbuffer, lastchar - cp);
249-
}
250-
*(--lastchar) = '\0';
251-
--cp;
252-
kdb_printf("\b%s \r", cp);
253-
tmp = *cp;
254-
*cp = '\0';
255-
kdb_printf(kdb_prompt_str);
256-
kdb_printf("%s", buffer);
257-
*cp = tmp;
272+
memmove(cp-1, cp, lastchar - cp + 1);
273+
lastchar--;
274+
cp--;
275+
kdb_printf("\b%s ", cp);
276+
kdb_position_cursor(kdb_prompt_str, buffer, cp);
258277
}
259278
break;
260279
case 10: /* linefeed */
@@ -269,22 +288,16 @@ static char *kdb_read(char *buffer, size_t bufsize)
269288
return buffer;
270289
case 4: /* Del */
271290
if (cp < lastchar) {
272-
memcpy(tmpbuffer, cp+1, lastchar - cp - 1);
273-
memcpy(cp, tmpbuffer, lastchar - cp - 1);
274-
*(--lastchar) = '\0';
275-
kdb_printf("%s \r", cp);
276-
tmp = *cp;
277-
*cp = '\0';
278-
kdb_printf(kdb_prompt_str);
279-
kdb_printf("%s", buffer);
280-
*cp = tmp;
291+
memmove(cp, cp+1, lastchar - cp);
292+
lastchar--;
293+
kdb_printf("%s ", cp);
294+
kdb_position_cursor(kdb_prompt_str, buffer, cp);
281295
}
282296
break;
283297
case 1: /* Home */
284298
if (cp > buffer) {
285-
kdb_printf("\r");
286-
kdb_printf(kdb_prompt_str);
287299
cp = buffer;
300+
kdb_position_cursor(kdb_prompt_str, buffer, cp);
288301
}
289302
break;
290303
case 5: /* End */
@@ -300,11 +313,10 @@ static char *kdb_read(char *buffer, size_t bufsize)
300313
}
301314
break;
302315
case 14: /* Down */
303-
memset(tmpbuffer, ' ',
304-
strlen(kdb_prompt_str) + (lastchar-buffer));
305-
*(tmpbuffer+strlen(kdb_prompt_str) +
306-
(lastchar-buffer)) = '\0';
307-
kdb_printf("\r%s\r", tmpbuffer);
316+
case 16: /* Up */
317+
kdb_printf("\r%*c\r",
318+
(int)(strlen(kdb_prompt_str) + (lastchar - buffer)),
319+
' ');
308320
*lastchar = (char)key;
309321
*(lastchar+1) = '\0';
310322
return lastchar;
@@ -314,33 +326,19 @@ static char *kdb_read(char *buffer, size_t bufsize)
314326
++cp;
315327
}
316328
break;
317-
case 16: /* Up */
318-
memset(tmpbuffer, ' ',
319-
strlen(kdb_prompt_str) + (lastchar-buffer));
320-
*(tmpbuffer+strlen(kdb_prompt_str) +
321-
(lastchar-buffer)) = '\0';
322-
kdb_printf("\r%s\r", tmpbuffer);
323-
*lastchar = (char)key;
324-
*(lastchar+1) = '\0';
325-
return lastchar;
326329
case 9: /* Tab */
327330
if (tab < 2)
328331
++tab;
329-
p_tmp = buffer;
330-
while (*p_tmp == ' ')
331-
p_tmp++;
332-
if (p_tmp > cp)
333-
break;
334-
memcpy(tmpbuffer, p_tmp, cp-p_tmp);
335-
*(tmpbuffer + (cp-p_tmp)) = '\0';
336-
p_tmp = strrchr(tmpbuffer, ' ');
337-
if (p_tmp)
338-
++p_tmp;
339-
else
340-
p_tmp = tmpbuffer;
341-
len = strlen(p_tmp);
342-
buf_size = sizeof(tmpbuffer) - (p_tmp - tmpbuffer);
343-
count = kallsyms_symbol_complete(p_tmp, buf_size);
332+
333+
tmp = *cp;
334+
*cp = '\0';
335+
p_tmp = strrchr(buffer, ' ');
336+
p_tmp = (p_tmp ? p_tmp + 1 : buffer);
337+
strscpy(tmpbuffer, p_tmp, sizeof(tmpbuffer));
338+
*cp = tmp;
339+
340+
len = strlen(tmpbuffer);
341+
count = kallsyms_symbol_complete(tmpbuffer, sizeof(tmpbuffer));
344342
if (tab == 2 && count > 0) {
345343
kdb_printf("\n%d symbols are found.", count);
346344
if (count > dtab_count) {
@@ -352,46 +350,51 @@ static char *kdb_read(char *buffer, size_t bufsize)
352350
}
353351
kdb_printf("\n");
354352
for (i = 0; i < count; i++) {
355-
ret = kallsyms_symbol_next(p_tmp, i, buf_size);
353+
ret = kallsyms_symbol_next(tmpbuffer, i, sizeof(tmpbuffer));
356354
if (WARN_ON(!ret))
357355
break;
358356
if (ret != -E2BIG)
359-
kdb_printf("%s ", p_tmp);
357+
kdb_printf("%s ", tmpbuffer);
360358
else
361-
kdb_printf("%s... ", p_tmp);
362-
*(p_tmp + len) = '\0';
359+
kdb_printf("%s... ", tmpbuffer);
360+
tmpbuffer[len] = '\0';
363361
}
364362
if (i >= dtab_count)
365363
kdb_printf("...");
366364
kdb_printf("\n");
367365
kdb_printf(kdb_prompt_str);
368366
kdb_printf("%s", buffer);
367+
if (cp != lastchar)
368+
kdb_position_cursor(kdb_prompt_str, buffer, cp);
369369
} else if (tab != 2 && count > 0) {
370-
len_tmp = strlen(p_tmp);
371-
strncpy(p_tmp+len_tmp, cp, lastchar-cp+1);
372-
len_tmp = strlen(p_tmp);
373-
strncpy(cp, p_tmp+len, len_tmp-len + 1);
374-
len = len_tmp - len;
375-
kdb_printf("%s", cp);
376-
cp += len;
377-
lastchar += len;
370+
/* How many new characters do we want from tmpbuffer? */
371+
len_tmp = strlen(tmpbuffer) - len;
372+
if (lastchar + len_tmp >= bufend)
373+
len_tmp = bufend - lastchar;
374+
375+
if (len_tmp) {
376+
/* + 1 ensures the '\0' is memmove'd */
377+
memmove(cp+len_tmp, cp, (lastchar-cp) + 1);
378+
memcpy(cp, tmpbuffer+len, len_tmp);
379+
kdb_printf("%s", cp);
380+
cp += len_tmp;
381+
lastchar += len_tmp;
382+
if (cp != lastchar)
383+
kdb_position_cursor(kdb_prompt_str,
384+
buffer, cp);
385+
}
378386
}
379387
kdb_nextline = 1; /* reset output line number */
380388
break;
381389
default:
382390
if (key >= 32 && lastchar < bufend) {
383391
if (cp < lastchar) {
384-
memcpy(tmpbuffer, cp, lastchar - cp);
385-
memcpy(cp+1, tmpbuffer, lastchar - cp);
386-
*++lastchar = '\0';
392+
memmove(cp+1, cp, lastchar - cp + 1);
393+
lastchar++;
387394
*cp = key;
388-
kdb_printf("%s\r", cp);
395+
kdb_printf("%s", cp);
389396
++cp;
390-
tmp = *cp;
391-
*cp = '\0';
392-
kdb_printf(kdb_prompt_str);
393-
kdb_printf("%s", buffer);
394-
*cp = tmp;
397+
kdb_position_cursor(kdb_prompt_str, buffer, cp);
395398
} else {
396399
*++lastchar = '\0';
397400
*cp++ = key;

kernel/debug/kdb/kdb_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2517,7 +2517,7 @@ static int kdb_summary(int argc, const char **argv)
25172517
if (val.uptime > (24*60*60)) {
25182518
int days = val.uptime / (24*60*60);
25192519
val.uptime %= (24*60*60);
2520-
kdb_printf("%d day%s ", days, days == 1 ? "" : "s");
2520+
kdb_printf("%d day%s ", days, str_plural(days));
25212521
}
25222522
kdb_printf("%02ld:%02ld\n", val.uptime/(60*60), (val.uptime/60)%60);
25232523

0 commit comments

Comments
 (0)