]> git.lizzy.rs Git - linenoise.git/commitdiff
Fix potential buffer overflow in utf8 mode
authorSteve Bennett <steveb@workware.net.au>
Sun, 31 Dec 2017 00:20:25 +0000 (10:20 +1000)
committerSteve Bennett <steveb@workware.net.au>
Sun, 31 Dec 2017 01:30:57 +0000 (11:30 +1000)
Define and use MAX_UTF8_LEN for the longest utf8 sequence
that can be returned.

In any case reverse incremental search was using + 3 rather
than - 3 to check for buffer overflow.

Also:
- support 4 byte utf8 sequences.
- better handling of invalid utf8 sequences

Signed-off-by: Steve Bennett <steveb@workware.net.au>
linenoise.c
utf8.c
utf8.h

index 171dcef4314819398931fd983b6a67dd6d786ea6..f65d3834170fd0e12ca8e4e9db9a144127ddfcd5 100644 (file)
@@ -360,7 +360,7 @@ static int fd_read_char(int fd, int timeout)
 static int fd_read(struct current *current)
 {
 #ifdef USE_UTF8
-    char buf[4];
+    char buf[MAX_UTF8_LEN];
     int n;
     int i;
     int c;
@@ -369,7 +369,7 @@ static int fd_read(struct current *current)
         return -1;
     }
     n = utf8_charlen(buf[0]);
-    if (n < 1 || n > 3) {
+    if (n < 1) {
         return -1;
     }
     for (i = 1; i < n; i++) {
@@ -377,7 +377,6 @@ static int fd_read(struct current *current)
             return -1;
         }
     }
-    buf[n] = 0;
     /* decode and return the character */
     utf8_tounicode(buf, &c);
     return c;
@@ -814,6 +813,7 @@ static int getWindowSize(struct current *current)
 }
 #endif
 
+#ifndef utf8_getchars
 static int utf8_getchars(char *buf, int c)
 {
 #ifdef USE_UTF8
@@ -823,6 +823,7 @@ static int utf8_getchars(char *buf, int c)
     return 1;
 #endif
 }
+#endif
 
 /**
  * Returns the unicode character at the given offset,
@@ -1006,7 +1007,7 @@ static int remove_char(struct current *current, int pos)
  */
 static int insert_char(struct current *current, int pos, int ch)
 {
-    char buf[3];
+    char buf[MAX_UTF8_LEN];
     int n = utf8_getchars(buf, ch);
 
     if (has_room(current, n) && pos >= 0 && pos <= current->chars) {
@@ -1319,7 +1320,8 @@ process_char:
                         skipsame = 1;
                     }
                     else if (c >= ' ') {
-                        if (rlen >= (int)sizeof(rbuf) + 3) {
+                        /* >= here to allow for null terminator */
+                        if (rlen >= (int)sizeof(rbuf) - MAX_UTF8_LEN) {
                             continue;
                         }
 
@@ -1383,7 +1385,7 @@ process_char:
             }
             break;
         case ctrl('V'):    /* ctrl-v */
-            if (has_room(current, 3)) {
+            if (has_room(current, MAX_UTF8_LEN)) {
                 /* Insert the ^V first */
                 if (insert_char(current, current->pos, c)) {
                     refreshLine(current->prompt, current);
diff --git a/utf8.c b/utf8.c
index 0edf450b09529a1cf5880d4057fab8e881489c20..cb30c49da6a2368f2b90071409e39ff5ec474bca 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -64,7 +64,7 @@ int utf8_strlen(const char *str, int bytelen)
     if (bytelen < 0) {
         bytelen = strlen(str);
     }
-    while (bytelen) {
+    while (bytelen > 0) {
         int c;
         int l = utf8_tounicode(str, &c);
         charlen++;
@@ -108,19 +108,28 @@ int utf8_tounicode(const char *str, int *uc)
     if (s[0] < 0xe0) {
         if ((s[1] & 0xc0) == 0x80) {
             *uc = ((s[0] & ~0xc0) << 6) | (s[1] & ~0x80);
-            return 2;
+            if (*uc >= 0x80) {
+                return 2;
+            }
+            /* Otherwise this is an invalid sequence */
         }
     }
     else if (s[0] < 0xf0) {
         if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80)) {
             *uc = ((s[0] & ~0xe0) << 12) | ((s[1] & ~0x80) << 6) | (s[2] & ~0x80);
-            return 3;
+            if (*uc >= 0x800) {
+                return 3;
+            }
+            /* Otherwise this is an invalid sequence */
         }
     }
     else if (s[0] < 0xf8) {
         if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80) && ((str[3] & 0xc0) == 0x80)) {
             *uc = ((s[0] & ~0xf0) << 18) | ((s[1] & ~0x80) << 12) | ((s[2] & ~0x80) << 6) | (s[3] & ~0x80);
-            return 4;
+            if (*uc >= 0x10000) {
+                return 4;
+            }
+            /* Otherwise this is an invalid sequence */
         }
     }
 
diff --git a/utf8.h b/utf8.h
index d4fb206bf27747e1c8cd17d9ed2ce5193b46f28b..df5223635f32121fc175c3b6e077e21bfa8b4d61 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -16,6 +16,8 @@ extern "C" {
 #ifndef USE_UTF8
 #include <ctype.h>
 
+#define MAX_UTF8_LEN 1
+
 /* No utf-8 support. 1 byte = 1 char */
 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
 #define utf8_strwidth(S, B) utf8_strlen((S), (B))
@@ -25,6 +27,9 @@ extern "C" {
  #define utf8_width(C) 1
 
 #else
+
+#define MAX_UTF8_LEN 4
+
 /**
  * Converts the given unicode codepoint (0 - 0x1fffff) to utf-8
  * and stores the result at 'p'.