This is a diff that should fix a few issues I've encountered with sftp's tab-complete, and a few others that I found in the process.
1) In makeargs: Lack of bounds checking for the argument pointer table, causing a buffer overflow when entering too many arguments. 2)Improper handling of absolute paths when PWD is part of the completed path. ex: sftp> cd /usr/local sftp> ls /usr/loc<tab> expected result: /usr/local/ actual result: /usr/loc 3)Improper handling of filenames containing escaped globbing characters. ex: touch "file*name" sftp> ls file\*<tab> expected result: file\*name actual result: file\*ame 4) * and # aren't getting escaped. ex: sftp> ls file<tab> expected result: file\*name actual result: file*name The character # causes makeargs to ignore the rest of the argument. So if you type "rm important_file#2" you will end up with the following arguments: [0] "rm" [1] "important_file" ex: sftp> rm important_file#2 Removing /tmp/important_file I haven't seen anywhere where this is documented. I added it to the characters to escape but I don't know why this behaviour is there to begin with. Index: sftp.c =================================================================== RCS file: /cvs/src/usr.bin/ssh/sftp.c,v retrieving revision 1.136 diff -u -r1.136 sftp.c --- sftp.c 22 Jun 2012 14:36:33 -0000 1.136 +++ sftp.c 11 Sep 2012 00:48:37 -0000 @@ -968,6 +968,10 @@ state = MA_START; i = j = 0; for (;;) { + if (argc >= sizeof(argv) / sizeof(*argv)){ + error("Too many arguments."); + return NULL; + } if (isspace(arg[i])) { if (state == MA_UNQUOTED) { /* Terminate current argument */ @@ -1671,7 +1675,7 @@ { glob_t g; char *tmp, *tmp2, ins[3]; - u_int i, hadglob, pwdlen, len, tmplen, filelen; + u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs; const LineInfo *lf; /* Glob from "file" location */ @@ -1680,6 +1684,9 @@ else xasprintf(&tmp, "%s*", file); + /* Check if the path is absolute. */ + isabs = tmp[0] == '/'; + memset(&g, 0, sizeof(g)); if (remote != LOCAL) { tmp = make_absolute(tmp, remote_path); @@ -1714,7 +1721,7 @@ goto out; tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); - tmp = path_strip(tmp2, remote_path); + tmp = path_strip(tmp2, isabs ? NULL : remote_path); xfree(tmp2); if (tmp == NULL) @@ -1723,8 +1730,24 @@ tmplen = strlen(tmp); filelen = strlen(file); - if (tmplen > filelen) { - tmp2 = tmp + filelen; + /* + *Count the number of escaped characters in the + *input string. + */ + cesc = isesc = 0; + for(i = 0; i < filelen; i++) + { + if (!isesc && file[i] == '\\' && i + 1 < filelen){ + isesc = 1; + cesc++; + } + else + isesc = 0; + } + + + if (tmplen > (filelen - cesc)) { + tmp2 = tmp + filelen - cesc; len = strlen(tmp2); /* quote argument on way out */ for (i = 0; i < len; i++) { @@ -1738,6 +1761,8 @@ case '\t': case '[': case ' ': + case '#': + case '*': if (quote == '\0' || tmp2[i] == quote) { if (el_insertstr(el, ins) == -1) fatal("el_insertstr "