]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: When tab-completing a filename, ensure that the completed
authordjm@openbsd.org <djm@openbsd.org>
Wed, 8 Oct 2025 21:48:40 +0000 (21:48 +0000)
committerDamien Miller <djm@mindrot.org>
Wed, 8 Oct 2025 23:07:06 +0000 (10:07 +1100)
string does not end up mid-way through a multibyte character, as this will
cause a fatal() later on.

based on GHPR#587 from @TaoistBrickscarrier; feedback tb@ kevlo@
ok dtucker@

OpenBSD-Commit-ID: efb977164b4e20d61204a66201a7592ba8291362

sftp.c

diff --git a/sftp.c b/sftp.c
index 3b505eea23db83b0a6edd2941945a81e384602be..57516a73a683285cf426a3a8d188c6d06d3392ed 100644 (file)
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp.c,v 1.245 2025/10/02 04:23:11 djm Exp $ */
+/* $OpenBSD: sftp.c,v 1.246 2025/10/08 21:48:40 djm Exp $ */
 /*
  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
  *
@@ -1863,29 +1863,44 @@ complete_display(char **list, u_int len)
 static char *
 complete_ambiguous(const char *word, char **list, size_t count)
 {
+       size_t i, j, matchlen;
+       char *tmp;
+       int len;
+
        if (word == NULL)
                return NULL;
 
-       if (count > 0) {
-               u_int y, matchlen = strlen(list[0]);
-
-               /* Find length of common stem */
-               for (y = 1; list[y]; y++) {
-                       u_int x;
-
-                       for (x = 0; x < matchlen; x++)
-                               if (list[0][x] != list[y][x])
-                                       break;
-
-                       matchlen = x;
-               }
+       if (count == 0)
+               return xstrdup(word); /* no options to complete */
 
-               if (matchlen > strlen(word)) {
-                       char *tmp = xstrdup(list[0]);
+       /* Find length of common stem across list */
+       matchlen = strlen(list[0]);
+       for (i = 1; i < count && list[i] != NULL; i++) {
+               for (j = 0; j < matchlen; j++)
+                       if (list[0][j] != list[i][j])
+                               break;
+               matchlen = j;
+       }
 
-                       tmp[matchlen] = '\0';
-                       return tmp;
-               }
+       /*
+        * Now check that the common stem doesn't finish in the middle of
+        * a multibyte character.
+        */
+       mblen(NULL, 0);
+       for (i = 0; i < matchlen;) {
+               len = mblen(list[0] + i, matchlen - i);
+               if (len <= 0 || i + (size_t)len > matchlen)
+                       break;
+               i += (size_t)len;
+       }
+       /* If so, truncate */
+       if (i < matchlen)
+               matchlen = i;
+
+       if (matchlen > strlen(word)) {
+               tmp = xstrdup(list[0]);
+               tmp[matchlen] = '\0';
+               return tmp;
        }
 
        return xstrdup(word);
@@ -2065,6 +2080,7 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
                tmp2 = tmp + filelen - cesc;
                len = strlen(tmp2);
                /* quote argument on way out */
+               mblen(NULL, 0);
                for (i = 0; i < len; i += clen) {
                        if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
                            (size_t)clen > sizeof(ins) - 2)