stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read()
@ 2024-04-24 14:03 Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 1/7] kdb: Fix buffer overflow during tab-complete Daniel Thompson
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Daniel Thompson @ 2024-04-24 14:03 UTC (permalink / raw)
  To: Jason Wessel, Douglas Anderson
  Cc: kgdb-bugreport, linux-kernel, Daniel Thompson, Justin Stitt, stable

Inspired by a patch from [Justin][1] I took a closer look at kdb_read().

Despite Justin's patch being a (correct) one-line manipulation it was a
tough patch to review because the surrounding code was hard to read and
it looked like there were unfixed problems.

This series isn't enough to make kdb_read() beautiful but it does make
it shorter, easier to reason about and fixes two buffer overflows and a
screen redraw problem!

[1]: https://lore.kernel.org/all/20240403-strncpy-kernel-debug-kdb-kdb_io-c-v1-1-7f78a08e9ff4@google.com/

Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
---
Changes in v3:
- Collected tags from v2
- Added comment to describe the hidden depths of kdb_position_cursor()
  (thanks Doug)
- Fixed a couple of typos in the patch descriptions (thanks Doug)
- Link to v2: https://lore.kernel.org/r/20240422-kgdb_read_refactor-v2-0-ed51f7d145fe@linaro.org

Changes in v2:
- No code changes!
- I belatedly realized that one of the cleanups actually fixed a buffer
  overflow so there are changes to Cc: (to add stable@...) and to one
  of the patch descriptions.
- Link to v1: https://lore.kernel.org/r/20240416-kgdb_read_refactor-v1-0-b18c2d01076d@linaro.org

---
Daniel Thompson (7):
      kdb: Fix buffer overflow during tab-complete
      kdb: Use format-strings rather than '\0' injection in kdb_read()
      kdb: Fix console handling when editing and tab-completing commands
      kdb: Merge identical case statements in kdb_read()
      kdb: Use format-specifiers rather than memset() for padding in kdb_read()
      kdb: Replace double memcpy() with memmove() in kdb_read()
      kdb: Simplify management of tmpbuffer in kdb_read()

 kernel/debug/kdb/kdb_io.c | 153 +++++++++++++++++++++++-----------------------
 1 file changed, 78 insertions(+), 75 deletions(-)
---
base-commit: dccce9b8780618986962ba37c373668bcf426866
change-id: 20240415-kgdb_read_refactor-2ea2dfc15dbb

Best regards,
-- 
Daniel Thompson <daniel.thompson@linaro.org>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 1/7] kdb: Fix buffer overflow during tab-complete
  2024-04-24 14:03 [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read() Daniel Thompson
@ 2024-04-24 14:03 ` Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 2/7] kdb: Use format-strings rather than '\0' injection in kdb_read() Daniel Thompson
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Daniel Thompson @ 2024-04-24 14:03 UTC (permalink / raw)
  To: Jason Wessel, Douglas Anderson
  Cc: kgdb-bugreport, linux-kernel, Daniel Thompson, Justin Stitt, stable

Currently, when the user attempts symbol completion with the Tab key, kdb
will use strncpy() to insert the completed symbol into the command buffer.
Unfortunately it passes the size of the source buffer rather than the
destination to strncpy() with predictably horrible results. Most obviously
if the command buffer is already full but cp, the cursor position, is in
the middle of the buffer, then we will write past the end of the supplied
buffer.

Fix this by replacing the dubious strncpy() calls with memmove()/memcpy()
calls plus explicit boundary checks to make sure we have enough space
before we start moving characters around.

Reported-by: Justin Stitt <justinstitt@google.com>
Closes: https://lore.kernel.org/all/CAFhGd8qESuuifuHsNjFPR-Va3P80bxrw+LqvC8deA8GziUJLpw@mail.gmail.com/
Cc: stable@vger.kernel.org
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Justin Stitt <justinstitt@google.com>
Tested-by: Justin Stitt <justinstitt@google.com>
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
---
 kernel/debug/kdb/kdb_io.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 9443bc63c5a24..06dfbccb10336 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -367,14 +367,19 @@ static char *kdb_read(char *buffer, size_t bufsize)
 			kdb_printf(kdb_prompt_str);
 			kdb_printf("%s", buffer);
 		} else if (tab != 2 && count > 0) {
-			len_tmp = strlen(p_tmp);
-			strncpy(p_tmp+len_tmp, cp, lastchar-cp+1);
-			len_tmp = strlen(p_tmp);
-			strncpy(cp, p_tmp+len, len_tmp-len + 1);
-			len = len_tmp - len;
-			kdb_printf("%s", cp);
-			cp += len;
-			lastchar += len;
+			/* How many new characters do we want from tmpbuffer? */
+			len_tmp = strlen(p_tmp) - len;
+			if (lastchar + len_tmp >= bufend)
+				len_tmp = bufend - lastchar;
+
+			if (len_tmp) {
+				/* + 1 ensures the '\0' is memmove'd */
+				memmove(cp+len_tmp, cp, (lastchar-cp) + 1);
+				memcpy(cp, p_tmp+len, len_tmp);
+				kdb_printf("%s", cp);
+				cp += len_tmp;
+				lastchar += len_tmp;
+			}
 		}
 		kdb_nextline = 1; /* reset output line number */
 		break;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 2/7] kdb: Use format-strings rather than '\0' injection in kdb_read()
  2024-04-24 14:03 [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read() Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 1/7] kdb: Fix buffer overflow during tab-complete Daniel Thompson
@ 2024-04-24 14:03 ` Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 3/7] kdb: Fix console handling when editing and tab-completing commands Daniel Thompson
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Daniel Thompson @ 2024-04-24 14:03 UTC (permalink / raw)
  To: Jason Wessel, Douglas Anderson
  Cc: kgdb-bugreport, linux-kernel, Daniel Thompson, stable, Justin Stitt

Currently when kdb_read() needs to reposition the cursor it uses copy and
paste code that works by injecting an '\0' at the cursor position before
delivering a carriage-return and reprinting the line (which stops at the
'\0').

Tidy up the code by hoisting the copy and paste code into an appropriately
named function. Additionally let's replace the '\0' injection with a
proper field width parameter so that the string will be abridged during
formatting instead.

Cc: stable@vger.kernel.org # Not a bug fix but it is needed for later bug fixes
Tested-by: Justin Stitt <justinstitt@google.com>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
---
 kernel/debug/kdb/kdb_io.c | 55 +++++++++++++++++++++++++++++------------------
 1 file changed, 34 insertions(+), 21 deletions(-)

diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 06dfbccb10336..50789c99b3ba8 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -184,6 +184,33 @@ char kdb_getchar(void)
 	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
  *
@@ -212,7 +239,6 @@ static char *kdb_read(char *buffer, size_t bufsize)
 						 * and null byte */
 	char *lastchar;
 	char *p_tmp;
-	char tmp;
 	static char tmpbuffer[CMD_BUFLEN];
 	int len = strlen(buffer);
 	int len_tmp;
@@ -249,12 +275,8 @@ static char *kdb_read(char *buffer, size_t bufsize)
 			}
 			*(--lastchar) = '\0';
 			--cp;
-			kdb_printf("\b%s \r", cp);
-			tmp = *cp;
-			*cp = '\0';
-			kdb_printf(kdb_prompt_str);
-			kdb_printf("%s", buffer);
-			*cp = tmp;
+			kdb_printf("\b%s ", cp);
+			kdb_position_cursor(kdb_prompt_str, buffer, cp);
 		}
 		break;
 	case 10: /* linefeed */
@@ -272,19 +294,14 @@ static char *kdb_read(char *buffer, size_t bufsize)
 			memcpy(tmpbuffer, cp+1, lastchar - cp - 1);
 			memcpy(cp, tmpbuffer, lastchar - cp - 1);
 			*(--lastchar) = '\0';
-			kdb_printf("%s \r", cp);
-			tmp = *cp;
-			*cp = '\0';
-			kdb_printf(kdb_prompt_str);
-			kdb_printf("%s", buffer);
-			*cp = tmp;
+			kdb_printf("%s ", cp);
+			kdb_position_cursor(kdb_prompt_str, buffer, cp);
 		}
 		break;
 	case 1: /* Home */
 		if (cp > buffer) {
-			kdb_printf("\r");
-			kdb_printf(kdb_prompt_str);
 			cp = buffer;
+			kdb_position_cursor(kdb_prompt_str, buffer, cp);
 		}
 		break;
 	case 5: /* End */
@@ -390,13 +407,9 @@ static char *kdb_read(char *buffer, size_t bufsize)
 				memcpy(cp+1, tmpbuffer, lastchar - cp);
 				*++lastchar = '\0';
 				*cp = key;
-				kdb_printf("%s\r", cp);
+				kdb_printf("%s", cp);
 				++cp;
-				tmp = *cp;
-				*cp = '\0';
-				kdb_printf(kdb_prompt_str);
-				kdb_printf("%s", buffer);
-				*cp = tmp;
+				kdb_position_cursor(kdb_prompt_str, buffer, cp);
 			} else {
 				*++lastchar = '\0';
 				*cp++ = key;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 3/7] kdb: Fix console handling when editing and tab-completing commands
  2024-04-24 14:03 [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read() Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 1/7] kdb: Fix buffer overflow during tab-complete Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 2/7] kdb: Use format-strings rather than '\0' injection in kdb_read() Daniel Thompson
@ 2024-04-24 14:03 ` Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 4/7] kdb: Merge identical case statements in kdb_read() Daniel Thompson
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Daniel Thompson @ 2024-04-24 14:03 UTC (permalink / raw)
  To: Jason Wessel, Douglas Anderson
  Cc: kgdb-bugreport, linux-kernel, Daniel Thompson, stable, Justin Stitt

Currently, if the cursor position is not at the end of the command buffer
and the user uses the Tab-complete functions, then the console does not
leave the cursor in the correct position.

For example consider the following buffer with the cursor positioned
at the ^:

md kdb_pro 10
          ^

Pressing tab should result in:

md kdb_prompt_str 10
                 ^

However this does not happen. Instead the cursor is placed at the end
(after then 10) and further cursor movement redraws incorrectly. The
same problem exists when we double-Tab but in a different part of the
code.

Fix this by sending a carriage return and then redisplaying the text to
the left of the cursor.

Cc: stable@vger.kernel.org
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Justin Stitt <justinstitt@google.com>
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
---
 kernel/debug/kdb/kdb_io.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 50789c99b3ba8..5fccb46f399e5 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -383,6 +383,8 @@ static char *kdb_read(char *buffer, size_t bufsize)
 			kdb_printf("\n");
 			kdb_printf(kdb_prompt_str);
 			kdb_printf("%s", buffer);
+			if (cp != lastchar)
+				kdb_position_cursor(kdb_prompt_str, buffer, cp);
 		} else if (tab != 2 && count > 0) {
 			/* How many new characters do we want from tmpbuffer? */
 			len_tmp = strlen(p_tmp) - len;
@@ -396,6 +398,9 @@ static char *kdb_read(char *buffer, size_t bufsize)
 				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 */

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 4/7] kdb: Merge identical case statements in kdb_read()
  2024-04-24 14:03 [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read() Daniel Thompson
                   ` (2 preceding siblings ...)
  2024-04-24 14:03 ` [PATCH v3 3/7] kdb: Fix console handling when editing and tab-completing commands Daniel Thompson
@ 2024-04-24 14:03 ` Daniel Thompson
  2024-04-24 14:03 ` [PATCH v3 5/7] kdb: Use format-specifiers rather than memset() for padding " Daniel Thompson
  2024-04-26 17:24 ` [PATCH v3 0/7] kdb: Refactor and fix bugs " Daniel Thompson
  5 siblings, 0 replies; 7+ messages in thread
From: Daniel Thompson @ 2024-04-24 14:03 UTC (permalink / raw)
  To: Jason Wessel, Douglas Anderson
  Cc: kgdb-bugreport, linux-kernel, Daniel Thompson, stable, Justin Stitt

The code that handles case 14 (down) and case 16 (up) has been copy and
pasted despite being byte-for-byte identical. Combine them.

Cc: stable@vger.kernel.org # Not a bug fix but it is needed for later bug fixes
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Justin Stitt <justinstitt@google.com>
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
---
 kernel/debug/kdb/kdb_io.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 5fccb46f399e5..a73779529803f 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -317,6 +317,7 @@ static char *kdb_read(char *buffer, size_t bufsize)
 		}
 		break;
 	case 14: /* Down */
+	case 16: /* Up */
 		memset(tmpbuffer, ' ',
 		       strlen(kdb_prompt_str) + (lastchar-buffer));
 		*(tmpbuffer+strlen(kdb_prompt_str) +
@@ -331,15 +332,6 @@ static char *kdb_read(char *buffer, size_t bufsize)
 			++cp;
 		}
 		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 */
 		if (tab < 2)
 			++tab;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 5/7] kdb: Use format-specifiers rather than memset() for padding in kdb_read()
  2024-04-24 14:03 [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read() Daniel Thompson
                   ` (3 preceding siblings ...)
  2024-04-24 14:03 ` [PATCH v3 4/7] kdb: Merge identical case statements in kdb_read() Daniel Thompson
@ 2024-04-24 14:03 ` Daniel Thompson
  2024-04-26 17:24 ` [PATCH v3 0/7] kdb: Refactor and fix bugs " Daniel Thompson
  5 siblings, 0 replies; 7+ messages in thread
From: Daniel Thompson @ 2024-04-24 14:03 UTC (permalink / raw)
  To: Jason Wessel, Douglas Anderson
  Cc: kgdb-bugreport, linux-kernel, Daniel Thompson, stable, Justin Stitt

Currently when the current line should be removed from the display
kdb_read() uses memset() to fill a temporary buffer with spaces.
The problem is not that this could be trivially implemented using a
format string rather than open coding it. The real problem is that
it is possible, on systems with a long kdb_prompt_str, to write past
the end of the tmpbuffer.

Happily, as mentioned above, this can be trivially implemented using a
format string. Make it so!

Cc: stable@vger.kernel.org
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Justin Stitt <justinstitt@google.com>
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
---
 kernel/debug/kdb/kdb_io.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index a73779529803f..2aeaf9765b248 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -318,11 +318,9 @@ static char *kdb_read(char *buffer, size_t bufsize)
 		break;
 	case 14: /* Down */
 	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);
+		kdb_printf("\r%*c\r",
+			   (int)(strlen(kdb_prompt_str) + (lastchar - buffer)),
+			   ' ');
 		*lastchar = (char)key;
 		*(lastchar+1) = '\0';
 		return lastchar;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read()
  2024-04-24 14:03 [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read() Daniel Thompson
                   ` (4 preceding siblings ...)
  2024-04-24 14:03 ` [PATCH v3 5/7] kdb: Use format-specifiers rather than memset() for padding " Daniel Thompson
@ 2024-04-26 17:24 ` Daniel Thompson
  5 siblings, 0 replies; 7+ messages in thread
From: Daniel Thompson @ 2024-04-26 17:24 UTC (permalink / raw)
  To: Jason Wessel, Douglas Anderson, Daniel Thompson
  Cc: kgdb-bugreport, linux-kernel, Justin Stitt, stable


On Wed, 24 Apr 2024 15:03:33 +0100, Daniel Thompson wrote:
> Inspired by a patch from [Justin][1] I took a closer look at kdb_read().
> 
> Despite Justin's patch being a (correct) one-line manipulation it was a
> tough patch to review because the surrounding code was hard to read and
> it looked like there were unfixed problems.
> 
> This series isn't enough to make kdb_read() beautiful but it does make
> it shorter, easier to reason about and fixes two buffer overflows and a
> screen redraw problem!
> 
> [...]

Applied, thanks!

[1/7] kdb: Fix buffer overflow during tab-complete
      commit: e9730744bf3af04cda23799029342aa3cddbc454
[2/7] kdb: Use format-strings rather than '\0' injection in kdb_read()
      commit: 09b35989421dfd5573f0b4683c7700a7483c71f9
[3/7] kdb: Fix console handling when editing and tab-completing commands
      commit: db2f9c7dc29114f531df4a425d0867d01e1f1e28
[4/7] kdb: Merge identical case statements in kdb_read()
      commit: 6244917f377bf64719551b58592a02a0336a7439
[5/7] kdb: Use format-specifiers rather than memset() for padding in kdb_read()
      commit: c9b51ddb66b1d96e4d364c088da0f1dfb004c574
[6/7] kdb: Replace double memcpy() with memmove() in kdb_read()
      commit: 80bd73c154e3063c4f9293163daf3262335f9f86
[7/7] kdb: Simplify management of tmpbuffer in kdb_read()
      commit: 64d504cfcd514743aaed3a5b79c060f0143149e9

Best regards,
-- 
Daniel Thompson <daniel.thompson@linaro.org>


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2024-04-26 17:24 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-24 14:03 [PATCH v3 0/7] kdb: Refactor and fix bugs in kdb_read() Daniel Thompson
2024-04-24 14:03 ` [PATCH v3 1/7] kdb: Fix buffer overflow during tab-complete Daniel Thompson
2024-04-24 14:03 ` [PATCH v3 2/7] kdb: Use format-strings rather than '\0' injection in kdb_read() Daniel Thompson
2024-04-24 14:03 ` [PATCH v3 3/7] kdb: Fix console handling when editing and tab-completing commands Daniel Thompson
2024-04-24 14:03 ` [PATCH v3 4/7] kdb: Merge identical case statements in kdb_read() Daniel Thompson
2024-04-24 14:03 ` [PATCH v3 5/7] kdb: Use format-specifiers rather than memset() for padding " Daniel Thompson
2024-04-26 17:24 ` [PATCH v3 0/7] kdb: Refactor and fix bugs " Daniel Thompson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).