diff --git a/llvm/include/llvm/Support/Program.h b/llvm/include/llvm/Support/Program.h --- a/llvm/include/llvm/Support/Program.h +++ b/llvm/include/llvm/Support/Program.h @@ -218,7 +218,7 @@ /// to build a single flat command line appropriate for calling CreateProcess /// on /// Windows. - std::string flattenWindowsCommandLine(ArrayRef Args); + ErrorOr flattenWindowsCommandLine(ArrayRef Args); #endif } } diff --git a/llvm/lib/Support/Windows/Program.inc b/llvm/lib/Support/Windows/Program.inc --- a/llvm/lib/Support/Windows/Program.inc +++ b/llvm/lib/Support/Windows/Program.inc @@ -189,7 +189,13 @@ // Windows wants a command line, not an array of args, to pass to the new // process. We have to concatenate them all, while quoting the args that // have embedded spaces (or are empty). - std::string Command = flattenWindowsCommandLine(Args); + auto Result = flattenWindowsCommandLine(Args); + if (std::error_code ec = Result.getError()) { + SetLastError(ec.value()); + MakeErrMsg(ErrMsg, std::string("Unable to convert command-line to UTF-16")); + return false; + } + std::wstring Command = *Result; // The pointer to the environment block for the new process. std::vector EnvBlock; @@ -271,14 +277,8 @@ return false; } - SmallVector CommandUtf16; - if (std::error_code ec = windows::UTF8ToUTF16(Command, CommandUtf16)) { - SetLastError(ec.value()); - MakeErrMsg(ErrMsg, - std::string("Unable to convert command-line to UTF-16")); - return false; - } - + std::vector CommandUtf16(Command.size() + 1, 0); + std::copy(Command.begin(), Command.end(), CommandUtf16.begin()); BOOL rc = CreateProcessW(ProgramUtf16.data(), CommandUtf16.data(), 0, 0, TRUE, CREATE_UNICODE_ENVIRONMENT, EnvBlock.empty() ? 0 : EnvBlock.data(), 0, &si, @@ -376,7 +376,7 @@ } namespace llvm { -std::string sys::flattenWindowsCommandLine(ArrayRef Args) { +ErrorOr sys::flattenWindowsCommandLine(ArrayRef Args) { std::string Command; for (StringRef Arg : Args) { if (argNeedsQuotes(Arg)) @@ -387,7 +387,11 @@ Command.push_back(' '); } - return Command; + SmallVector CommandUtf16; + if (std::error_code ec = windows::UTF8ToUTF16(Command, CommandUtf16)) + return ec; + + return std::wstring(CommandUtf16.begin(), CommandUtf16.end()); } ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, @@ -532,12 +536,16 @@ bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, ArrayRef Args) { - // The documented max length of the command line passed to CreateProcess. - static const size_t MaxCommandStringLength = 32768; + // The documentation on CreateProcessW states that the size of the argument + // lpCommandLine must not be greater than 32767 characters, including the + // Unicode terminating null character. We use smaller value to reduce risk + // of getting invalid command line due to unaccounted factors. + static const size_t MaxCommandStringLength = 32000; SmallVector FullArgs; FullArgs.push_back(Program); FullArgs.append(Args.begin(), Args.end()); - std::string Result = flattenWindowsCommandLine(FullArgs); - return (Result.size() + 1) <= MaxCommandStringLength; + auto Result = flattenWindowsCommandLine(FullArgs); + assert(!Result.getError()); + return (Result->size() + 1) <= MaxCommandStringLength; } } diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -763,6 +763,17 @@ TEST(CommandLineTest, ArgumentLimit) { std::string args(32 * 4096, 'a'); EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data())); + std::string args2(256, 'a'); + EXPECT_TRUE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args2.data())); + if (Triple(sys::getProcessTriple()).isOSWindows()) { + // We use 32000 as a limit for command line length. + std::string long_arg(32000, 'b'); + EXPECT_TRUE( + llvm::sys::commandLineFitsWithinSystemLimits("cl", long_arg.data())); + long_arg += 'b'; + EXPECT_FALSE( + llvm::sys::commandLineFitsWithinSystemLimits("cl", long_arg.data())); + } } TEST(CommandLineTest, ResponseFileWindows) {