diff --git a/ChangeLog b/ChangeLog
index 9d1e7f61092c4c047b59e44cf78cf7fe24b7ebd5..c1f55573cc363eda9c6ab504fc760bdd35a0cd5c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -37,6 +37,9 @@ CVS code -
 	  Mike Frysinger)
   display_string()
 	- Fix minor memory leak. (DLR)
+	- Fix memory corruption problems caused by not allocating enough
+	  space for converted when a line ends in a tab(s) and we're not
+	  in UTF-8 mode. (DLR, found by Nick Warne and Mike Frysinger)
 - doc/nano.1:
 	- Better display the default values for quotestr. (DLR)
 - doc/nanorc.5:
diff --git a/src/winio.c b/src/winio.c
index 4a3c8a0abeb6a2c0345f750c22b57a4e57a6401d..b0897aca0a8cf54c16993d9db8599d203970aa42 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -1809,10 +1809,22 @@ char *display_string(const char *buf, size_t start_col, size_t len, bool
 
     assert(column <= start_col);
 
-    /* Allocate enough space for the entire line. */
-    alloc_len = (mb_cur_max() * (COLS + 1));
+    /* Make sure there's enough room for the initial character, whether
+     * it's a multibyte control character, a non-control multibyte
+     * character, a tab character, or a null terminator.  Rationale:
+     *
+     * multibyte control character followed by a null terminator:
+     *     1 byte ('^') + mb_cur_max() bytes + 1 byte ('\0')
+     * multibyte non-control character followed by a null terminator:
+     *     mb_cur_max() bytes + 1 byte ('\0')
+     * tab character followed by a null terminator:
+     *     mb_cur_max() bytes + (tabsize - 1) bytes + 1 byte ('\0')
+     *
+     * Since tabsize has a minimum value of 1, it can substitute for 1
+     * byte above. */
+    alloc_len = (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
+    converted = charalloc(alloc_len);
 
-    converted = charalloc(alloc_len + 1);
     index = 0;
 
     if (buf[start_index] != '\t' && (column < start_col || (dollars &&
@@ -1849,9 +1861,17 @@ char *display_string(const char *buf, size_t start_col, size_t len, bool
 #endif
     }
 
-    while (index < alloc_len - 1 && buf[start_index] != '\0') {
+    while (buf[start_index] != '\0') {
 	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
 
+	/* Make sure there's enough room for the next character, whether
+	 * it's a multibyte control character, a non-control multibyte
+	 * character, a tab character, or a null terminator. */
+	if (index + mb_cur_max() + tabsize + 1 >= alloc_len - 1) {
+	    alloc_len += (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
+	    converted = charealloc(converted, alloc_len);
+	}
+
 	/* If buf contains a tab character, interpret it. */
 	if (*buf_mb == '\t') {
 #if !defined(NANO_TINY) && defined(ENABLE_NANORC)
@@ -1923,8 +1943,10 @@ char *display_string(const char *buf, size_t start_col, size_t len, bool
 
     free(buf_mb);
 
-    if (index < alloc_len - 1)
-	converted[index] = '\0';
+    assert(alloc_len >= index + 1);
+
+    /* Null terminate converted. */
+    converted[index] = '\0';
 
     /* Make sure converted takes up no more than len columns. */
     index = actual_x(converted, len);