From 1826095a5faa4303c9fb5d3c5aadc46bf66c1cd6 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai Date: Tue, 16 Apr 2019 00:18:50 +0000 Subject: [PATCH] Reapply [Support] Fix recursive response file expansion guard The test in the dependent revision has been fixed for Windows. Original commit message: Response file expansion limits the amount of expansion to prevent potential infinite recursion. However, the current logic assumes that any argument beginning with @ is a response file, which is not true for e.g. `-Xlinker -rpath -Xlinker @executable_path/../lib` on Darwin. Having too many of these non-response file arguments beginning with @ prevents actual response files from being expanded. Instead, limit based on the number of successful response file expansions, which should still prevent infinite recursion but also avoid false positives. Differential Revision: https://reviews.llvm.org/D60631 > llvm-svn: 358452 llvm-svn: 358466 --- llvm/lib/Support/CommandLine.cpp | 10 +++--- llvm/unittests/Support/CommandLineTest.cpp | 37 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index 98d06f65c798..0050002ff054 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -1040,7 +1040,7 @@ static bool ExpandResponseFile(StringRef FName, StringSaver &Saver, bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, SmallVectorImpl &Argv, bool MarkEOLs, bool RelativeNames) { - unsigned RspFiles = 0; + unsigned ExpandedRspFiles = 0; bool AllExpanded = true; // Don't cache Argv.size() because it can change. @@ -1058,14 +1058,16 @@ bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, // If we have too many response files, leave some unexpanded. This avoids // crashing on self-referential response files. - if (RspFiles++ > 20) + if (ExpandedRspFiles > 20) return false; // Replace this response file argument with the tokenization of its // contents. Nested response files are expanded in subsequent iterations. SmallVector ExpandedArgv; - if (!ExpandResponseFile(Arg + 1, Saver, Tokenizer, ExpandedArgv, - MarkEOLs, RelativeNames)) { + if (ExpandResponseFile(Arg + 1, Saver, Tokenizer, ExpandedArgv, MarkEOLs, + RelativeNames)) { + ++ExpandedRspFiles; + } else { // We couldn't read this file, so we leave it in the argument stream and // move on. AllExpanded = false; diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp index 263d0e30191b..b648600b9080 100644 --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -816,6 +816,43 @@ TEST(CommandLineTest, RecursiveResponseFiles) { EXPECT_STREQ(Argv[i], ResponseFileRef.c_str()); } +TEST(CommandLineTest, ResponseFilesAtArguments) { + SmallString<128> TestDir; + std::error_code EC = sys::fs::createUniqueDirectory("unittest", TestDir); + EXPECT_TRUE(!EC); + + SmallString<128> ResponseFilePath; + sys::path::append(ResponseFilePath, TestDir, "test.rsp"); + + std::ofstream ResponseFile(ResponseFilePath.c_str()); + EXPECT_TRUE(ResponseFile.is_open()); + ResponseFile << "-foo" << "\n"; + ResponseFile << "-bar" << "\n"; + ResponseFile.close(); + + // Ensure we expand rsp files after lots of non-rsp arguments starting with @. + constexpr size_t NON_RSP_AT_ARGS = 64; + SmallVector Argv = {"test/test"}; + Argv.append(NON_RSP_AT_ARGS, "@non_rsp_at_arg"); + std::string ResponseFileRef = std::string("@") + ResponseFilePath.c_str(); + Argv.push_back(ResponseFileRef.c_str()); + + BumpPtrAllocator A; + StringSaver Saver(A); + bool Res = cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv, + false, false); + EXPECT_FALSE(Res); + + // ASSERT instead of EXPECT to prevent potential out-of-bounds access. + ASSERT_EQ(Argv.size(), 1 + NON_RSP_AT_ARGS + 2); + size_t i = 0; + EXPECT_STREQ(Argv[i++], "test/test"); + for (; i < 1 + NON_RSP_AT_ARGS; ++i) + EXPECT_STREQ(Argv[i], "@non_rsp_at_arg"); + EXPECT_STREQ(Argv[i++], "-foo"); + EXPECT_STREQ(Argv[i++], "-bar"); +} + TEST(CommandLineTest, SetDefautValue) { cl::ResetCommandLineParser();