microsoft / STL

MSVC's implementation of the C++ Standard Library.
Other
10.05k stars 1.48k forks source link

`<filesystem>`: `directory_iterator` with `\\\\.\\pipe` can produce invalid paths #4423

Open diinngg opened 6 months ago

diinngg commented 6 months ago

Describe the bug

Attempting to use directory_iterator or recursive_directory_iterator with the path \\\\.\\pipe (to list named pipes) can produce invalid paths. It seems that although the documentation states that pipe names can't include backslashes, this isn't actually enforced. If a pipe under \\\\.\\pipe\\ does include a backslash in its name then _Dir_enum_impl::_Refresh only replaces the previous pipe name from the last backslash onwards, producing paths like "\\\\.\\pipe\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\def", where a previously found pipe was named "\\\\.\\pipe\\WinSock2\\abc".

Command-line test case

C:\DirectoryIteratorTest>type DirectoryIteratorTest.cpp
#include <filesystem>
#include <iostream>

#include <Windows.h>

int main()
{
        // Random strings that shouldn't already exist in pipe names
        const std::wstring strRand1{ L"5639357256" }, strRand2{ L"9423298489" }, strRand3{ L"3214769820" };

        // The base path for pipes
        const std::filesystem::path pathPipeBase{ "\\\\.\\pipe" };
        // A pipe path with a backslash in its name
        const std::filesystem::path pathPipeBad{ pathPipeBase / strRand1 / strRand2 };
        // A pipe path without a backslash in its name
        const std::filesystem::path pathPipeOk{ pathPipeBase / strRand3 };

        HANDLE hPipeBad(CreateNamedPipeW(pathPipeBad.native().c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, nullptr));
        HANDLE hPipeOk(CreateNamedPipeW(pathPipeOk.native().c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, nullptr));
        if (!std::filesystem::exists(pathPipeBad) || !std::filesystem::exists(pathPipeOk))
        {
                std::cout << "CreateNamedPipe failed" << std::endl;
                return 1;
        }

        for (const auto& entry : std::filesystem::directory_iterator{ pathPipeBase })
        {
                // Look for paths that contain the random string in the bad path before the backslash, but not the one after
                if ((entry.path().native().find(strRand1) != std::wstring::npos) && (entry.path().native().find(strRand2) == std::wstring::npos))
                        std::cout << entry.path() << std::endl;
        }

        CloseHandle(hPipeOk);
        CloseHandle(hPipeBad);
}

C:\DirectoryIteratorTest>cl /EHsc /W4 /WX /std:c++latest DirectoryIteratorTest.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.40.33521 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

/std:c++latest is provided as a preview of language features from the latest C++
working draft, and we're eager to hear about bugs and suggestions for improvements.
However, note that these features are provided as-is without support, and subject
to changes or removal as the working draft evolves. See
https://go.microsoft.com/fwlink/?linkid=2045807 for details.

DirectoryIteratorTest.cpp
Microsoft (R) Incremental Linker Version 14.40.33521.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:DirectoryIteratorTest.exe
DirectoryIteratorTest.obj

C:\DirectoryIteratorTest>.\DirectoryIteratorTest.exe
"\\\\.\\pipe\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\Winsock2\\PIPE_EVENTROOT\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\ProtectedPrefix\\LocalService\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\uv\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\IdentityNexusIntegrationPipe\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\LOCAL\\5639357256\\3214769820"

Expected behavior

In the example above there should be no output, in general pipe names with backslashes should be handled correctly

STL version

Microsoft Visual Studio Professional 2022 (64-bit) - Preview Version 17.10.0 Preview 1.0

Additional context

I have no idea whether supporting \\\\.\\pipe with std::filesystem is even intended, though it would be nice

diinngg commented 6 months ago

See https://github.com/diinngg/STL/commit/87e9619bd66fcf46266fc0d27027630a0d072be6 for an example fix, though it's mostly untested

StephanTLavavej commented 6 months ago

We talked about this at the weekly maintainer meeting and we want to follow up with the Windows team to see whether we should actually support pipes with backslashes in their names, or if they need to fix the API to properly reject this as documented.