summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/cmdAH.test3
-rw-r--r--unix/tclUnixFile.c6
-rw-r--r--win/tclWinFile.c63
3 files changed, 70 insertions, 2 deletions
diff --git a/tests/cmdAH.test b/tests/cmdAH.test
index dc61ac6..4ca90c6 100644
--- a/tests/cmdAH.test
+++ b/tests/cmdAH.test
@@ -104,6 +104,9 @@ test cmdAH-2.6.1 {Tcl_CdObjCmd} {
list [catch {cd ""} msg] $msg
} {1 {couldn't change working directory to "": no such file or directory}}
+test cmdAH-2.6.3 {Tcl_CdObjCmd, bug #3118489} -returnCodes error -body {
+ cd .\0
+} -result "couldn't change working directory to \".\0\": no such file or directory"
test cmdAH-2.7 {Tcl_ConcatObjCmd} {
concat
} {}
diff --git a/unix/tclUnixFile.c b/unix/tclUnixFile.c
index 29f1aba..c5f75a7 100644
--- a/unix/tclUnixFile.c
+++ b/unix/tclUnixFile.c
@@ -1111,6 +1111,12 @@ TclNativeCreateNativeRep(
str = Tcl_GetStringFromObj(validPathPtr, &len);
Tcl_UtfToExternalDString(NULL, str, len, &ds);
len = Tcl_DStringLength(&ds) + sizeof(char);
+ if (strlen(Tcl_DStringValue(&ds)) < len - sizeof(char)) {
+ /* See bug [3118489]: NUL in filenames */
+ Tcl_DecrRefCount(validPathPtr);
+ Tcl_DStringFree(&ds);
+ return NULL;
+ }
Tcl_DecrRefCount(validPathPtr);
nativePathPtr = ckalloc((unsigned) len);
memcpy((void*)nativePathPtr, (void*)Tcl_DStringValue(&ds), (size_t) len);
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index ed0c40f..9bf63b1 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -1856,6 +1856,9 @@ TclpObjChdir(
nativePath = (const TCHAR *) Tcl_FSGetNativePath(pathPtr);
+ if (!nativePath) {
+ return -1;
+ }
result = (*tclWinProcs->setCurrentDirectoryProc)(nativePath);
if (result == 0) {
@@ -3200,13 +3203,69 @@ TclNativeCreateNativeRep(
Tcl_WinUtfToTChar(str, len, &ds);
if (tclWinProcs->useWide) {
WCHAR *wp = (WCHAR *) Tcl_DStringValue(&ds);
- for (; *wp; ++wp) {
- if (*wp=='/') {
+ len = Tcl_DStringLength(&ds)>>1;
+ /*
+ ** If path starts with "//?/" or "\\?\" (extended path), translate
+ ** any slashes to backslashes but accept the '?' as being valid.
+ */
+ if ((str[0]=='\\' || str[0]=='/') && (str[1]=='\\' || str[1]=='/')
+ && str[2]=='?' && (str[3]=='\\' || str[3]=='/')) {
+ wp[0] = wp[1] = wp[3] = '\\';
+ str += 4;
+ wp += 4;
+ len -= 4;
+ }
+ /*
+ ** If there is a drive prefix, the ':' must be considered valid.
+ **/
+ if (((str[0]>='A'&&str[0]<='Z') || (str[0]>='a'&&str[0]<='z'))
+ && str[1]==':') {
+ wp += 2;
+ len -= 2;
+ }
+ while (len-->0) {
+ if ((*wp < ' ') || wcschr(L"\"*:<>?|", *wp)) {
+ Tcl_DecrRefCount(validPathPtr);
+ Tcl_DStringFree(&ds);
+ return NULL;
+ } else if (*wp=='/') {
*wp = '\\';
}
+ ++wp;
}
len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
} else {
+ char *p = Tcl_DStringValue(&ds);
+ len = Tcl_DStringLength(&ds);
+ /*
+ ** If path starts with "//?/" or "\\?\" (extended path), translate
+ ** any slashes to backslashes but accept the '?' as being valid.
+ */
+ if ((str[0]=='\\' || str[0]=='/') && (str[1]=='\\' || str[1]=='/')
+ && str[2]=='?' && (str[3]=='\\' || str[3]=='/')) {
+ p[0] = p[1] = p[3] = '\\';
+ str += 4;
+ p += 4;
+ len -= 4;
+ }
+ /*
+ ** If there is a drive prefix, the ':' must be considered valid.
+ **/
+ if (((str[0]>='A'&&str[0]<='Z') || (str[0]>='a'&&str[0]<='z'))
+ && str[1]==':') {
+ p += 2;
+ len -= 2;
+ }
+ while (len-->0) {
+ if ((*p < ' ') || strchr("\"*:<>?|", *p)) {
+ Tcl_DecrRefCount(validPathPtr);
+ Tcl_DStringFree(&ds);
+ return NULL;
+ } else if (*p=='/') {
+ *p = '\\';
+ }
+ ++p;
+ }
len = Tcl_DStringLength(&ds) + sizeof(char);
}
Tcl_DecrRefCount(validPathPtr);