Fix Path.GetTempFileName on Windows (#17148)
authorStephen Toub <stoub@microsoft.com>
Fri, 23 Mar 2018 16:41:21 +0000 (12:41 -0400)
committerGitHub <noreply@github.com>
Fri, 23 Mar 2018 16:41:21 +0000 (12:41 -0400)
GetTempFileNameW doesn't return the required length of the buffer; it returns the unique number used in the temporary file name.  But the calling code is using that result as a length, resulting in sometimes truncated paths, and sometimes paths that aren't at all related to the original due to state left over in a pooled buffer.

src/mscorlib/shared/System/IO/Path.Windows.cs
src/mscorlib/shared/System/Text/ValueStringBuilder.cs

index 8f1bed0..f22a991 100644 (file)
@@ -165,20 +165,15 @@ namespace System.IO
             Span<char> initialBuffer = stackalloc char[PathInternal.MaxShortPath];
             var builder = new ValueStringBuilder(initialBuffer);
 
-            uint result = 0;
-            while ((result = Interop.Kernel32.GetTempFileNameW(
-                ref tempPathBuilder.GetPinnableReference(), "tmp", 0, ref builder.GetPinnableReference())) > builder.Capacity)
-            {
-                // Reported size is greater than the buffer size. Increase the capacity.
-                builder.EnsureCapacity(checked((int)result));
-            }
+            uint result = Interop.Kernel32.GetTempFileNameW(
+                ref tempPathBuilder.GetPinnableReference(), "tmp", 0, ref builder.GetPinnableReference());
 
             tempPathBuilder.Dispose();
 
             if (result == 0)
                 throw Win32Marshal.GetExceptionForLastWin32Error();
 
-            builder.Length = (int)result;
+            builder.Length = builder.RawChars.IndexOf('\0');
 
             string path = PathHelper.Normalize(ref builder);
             builder.Dispose();
index b1abdd5..d41bea0 100644 (file)
@@ -27,6 +27,7 @@ namespace System.Text
             get => _pos;
             set
             {
+                Debug.Assert(value >= 0);
                 Debug.Assert(value <= _chars.Length);
                 _pos = value;
             }
@@ -70,6 +71,9 @@ namespace System.Text
             return s;
         }
 
+        /// <summary>Returns the underlying storage of the builder.</summary>
+        public Span<char> RawChars => _chars;
+
         /// <summary>
         /// Returns a span around the contents of the builder.
         /// </summary>