[Windows] Fix cmd line tokenization of unclosed quotes.

When cl::TokenizeWindowsCommandLine received a command line with an
unterminated double-quoted string at the end, it would discard the
text within that string. That doesn't match the behavior of the
standard Windows C library, which will return the text in the unclosed
quoted string as an argv word.

Fixed, and added extra unit tests in that area.

In some cases (specifically the one in Bugzilla #47579) this could
cause TokenizeWindowsCommandLine to return a zero-length list of
arguments, leading to an array overrun at the call site in
windows::GetCommandLineArguments. Added a check there, for extra
safety: now windows::GetCommandLineArguments will return an error code
instead of failing an assertion.

(This change was written as part of https://reviews.llvm.org/D122914,
but split into a separate commit at the last minute at the code
reviewer's suggestion, because it's fixing an unrelated bug in the
same area. The rest of D122914 will follow in the next commit.)
This commit is contained in:
Simon Tatham 2022-05-03 10:33:11 +01:00
parent 0a1bcab9f3
commit 1be024ee45
3 changed files with 17 additions and 2 deletions

View File

@ -1008,7 +1008,7 @@ tokenizeWindowsCommandLineImpl(StringRef Src, StringSaver &Saver,
}
}
if (State == UNQUOTED)
if (State != INIT)
AddToken(Saver.save(Token.str()));
}

View File

@ -255,6 +255,9 @@ windows::GetCommandLineArguments(SmallVectorImpl<const char *> &Args,
return EC;
}
if (Args.size() == 0)
return std::make_error_code(std::errc::invalid_argument);
SmallVector<char, MAX_PATH> Arg0(Args[0], Args[0] + strlen(Args[0]));
SmallVector<char, MAX_PATH> Filename;
sys::path::remove_filename(Arg0);

View File

@ -237,12 +237,24 @@ TEST(CommandLineTest, TokenizeWindowsCommandLine2) {
}
TEST(CommandLineTest, TokenizeWindowsCommandLineQuotedLastArgument) {
// Whitespace at the end of the command line doesn't cause an empty last word
const char Input0[] = R"(a b c d )";
const char *const Output0[] = {"a", "b", "c", "d"};
testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input0, Output0);
// But an explicit "" does
const char Input1[] = R"(a b c d "")";
const char *const Output1[] = {"a", "b", "c", "d", ""};
testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input1, Output1);
// An unterminated quoted string is also emitted as an argument word, empty
// or not
const char Input2[] = R"(a b c d ")";
const char *const Output2[] = {"a", "b", "c", "d"};
const char *const Output2[] = {"a", "b", "c", "d", ""};
testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input2, Output2);
const char Input3[] = R"(a b c d "text)";
const char *const Output3[] = {"a", "b", "c", "d", "text"};
testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input3, Output3);
}
TEST(CommandLineTest, TokenizeAndMarkEOLs) {