<html>
<head>
<base href="https://bugs.llvm.org/">
</head>
<body><table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Bug ID</th>
<td><a class="bz_bug_link
bz_status_NEW "
title="NEW - clang-cl /clang: pass-through causes read-after-free with aliased options"
href="https://bugs.llvm.org/show_bug.cgi?id=42501">42501</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>clang-cl /clang: pass-through causes read-after-free with aliased options
</td>
</tr>
<tr>
<th>Product</th>
<td>clang
</td>
</tr>
<tr>
<th>Version</th>
<td>trunk
</td>
</tr>
<tr>
<th>Hardware</th>
<td>PC
</td>
</tr>
<tr>
<th>OS</th>
<td>All
</td>
</tr>
<tr>
<th>Status</th>
<td>NEW
</td>
</tr>
<tr>
<th>Severity</th>
<td>enhancement
</td>
</tr>
<tr>
<th>Priority</th>
<td>P
</td>
</tr>
<tr>
<th>Component</th>
<td>Frontend
</td>
</tr>
<tr>
<th>Assignee</th>
<td>unassignedclangbugs@nondot.org
</td>
</tr>
<tr>
<th>Reporter</th>
<td>nicolasweber@gmx.de
</td>
</tr>
<tr>
<th>CC</th>
<td>llvm-bugs@lists.llvm.org, neeilans@live.com, richard-llvm@metafoo.co.uk
</td>
</tr></table>
<p>
<div>
<pre>Repro:
1. Build clang with asan
2. Run `bin/clang-cl /clang:-fprofile-remapping-file /clang:adsf -c test.cc /c`
(test.cc is just an empty file)
This results in:
=================================================================
==64401==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000002f50
at pc 0x000114337c03 bp 0x7ffeec1aaee0 sp 0x7ffeec1aa6a8
READ of size 25 at 0x603000002f50 thread T0
#0 0x114337c02 in __asan_memcpy
(libclang_rt.asan_osx_dynamic.dylib:x86_64+0x40c02)
#1 0x106eb33b1 in llvm::raw_svector_ostream::write_impl(char const*,
unsigned long) (clang-cl:x86_64+0x1034683b1)
#2 0x106eaaafa in llvm::raw_ostream::write(char const*, unsigned long)
(clang-cl:x86_64+0x10345fafa)
#3 0x106e5c8a0 in llvm::Twine::printOneChild(llvm::raw_ostream&,
llvm::Twine::Child, llvm::Twine::NodeKind) const (clang-cl:x86_64+0x1034118a0)
#4 0x106e5bc12 in llvm::Twine::toVector(llvm::SmallVectorImpl<char>&) const
(clang-cl:x86_64+0x103410c12)
#5 0x106c028dd in llvm::opt::ArgList::MakeArgString(llvm::Twine const&)
const (clang-cl:x86_64+0x1031b78dd)
#6 0x106c0352c in llvm::opt::ArgList::GetOrMakeJoinedArgString(unsigned
int, llvm::StringRef, llvm::StringRef) const (clang-cl:x86_64+0x1031b852c)
#7 0x106bfc77c in llvm::opt::Arg::render(llvm::opt::ArgList const&,
llvm::SmallVector<char const*, 16u>&) const (clang-cl:x86_64+0x1031b177c)
#8 0x104996f49 in
clang::driver::tools::Clang::ConstructJob(clang::driver::Compilation&,
clang::driver::JobAction const&, clang::driver::InputInfo const&,
llvm::SmallVector<clang::driver::InputInfo, 4u> const&, llvm::opt::ArgList
const&, char const*) const (clang-cl:x86_64+0x100f4bf49)
#9 0x104869534 in
clang::driver::Driver::BuildJobsForActionNoCache(clang::driver::Compilation&,
clang::driver::Action const*, clang::driver::ToolChain const*, llvm::StringRef,
bool, bool, char const*, std::__1::map<std::__1::pair<clang::driver::Action
const*, std::__1::basic_string<char, std::__1::char_traits<char>,
std::__1::allocator<char> > >, clang::driver::InputInfo,
std::__1::less<std::__1::pair<clang::driver::Action const*,
std::__1::basic_string<char, std::__1::char_traits<char>,
std::__1::allocator<char> > > >,
std::__1::allocator<std::__1::pair<std::__1::pair<clang::driver::Action const*,
std::__1::basic_string<char, std::__1::char_traits<char>,
std::__1::allocator<char> > > const, clang::driver::InputInfo> > >&,
clang::driver::Action::OffloadKind) const (clang-cl:x86_64+0x100e1e534)
#10 0x10483d8a7 in
clang::driver::Driver::BuildJobs(clang::driver::Compilation&) const
(clang-cl:x86_64+0x100df28a7)
#11 0x10481f339 in
clang::driver::Driver::BuildCompilation(llvm::ArrayRef<char const*>)
(clang-cl:x86_64+0x100dd4339)
#12 0x103a7ad2e in main (clang-cl:x86_64+0x10002fd2e)
#13 0x7fff7928b3d4 in start (libdyld.dylib:x86_64+0x163d4)
0x603000002f50 is located 0 bytes inside of 32-byte region
[0x603000002f50,0x603000002f70)
freed by thread T0 here:
#0 0x114346a4d (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x4fa4d)
#1 0x103a66323 in llvm::opt::InputArgList::~InputArgList()
(clang-cl:x86_64+0x10001b323)
#2 0x10481beb0 in
clang::driver::Driver::BuildCompilation(llvm::ArrayRef<char const*>)
(clang-cl:x86_64+0x100dd0eb0)
#3 0x103a7ad2e in main (clang-cl:x86_64+0x10002fd2e)
#4 0x7fff7928b3d4 in start (libdyld.dylib:x86_64+0x163d4)
previously allocated by thread T0 here:
#0 0x11434662d (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x4f62d)
#1 0x106c045ac in llvm::opt::InputArgList::MakeIndex(llvm::StringRef) const
(clang-cl:x86_64+0x1031b95ac)
#2 0x106c04b18 in
llvm::opt::InputArgList::MakeArgStringRef(llvm::StringRef) const
(clang-cl:x86_64+0x1031b9b18)
#3 0x106c161e7 in llvm::opt::Option::accept(llvm::opt::ArgList const&,
unsigned int&, unsigned int) const (clang-cl:x86_64+0x1031cb1e7)
#4 0x106c0f7d0 in llvm::opt::OptTable::ParseOneArg(llvm::opt::ArgList
const&, unsigned int&, unsigned int, unsigned int) const
(clang-cl:x86_64+0x1031c47d0)
#5 0x106c1012b in llvm::opt::OptTable::ParseArgs(llvm::ArrayRef<char
const*>, unsigned int&, unsigned int&, unsigned int, unsigned int) const
(clang-cl:x86_64+0x1031c512b)
#6 0x1047fdbde in
clang::driver::Driver::ParseArgStrings(llvm::ArrayRef<char const*>, bool,
bool&) (clang-cl:x86_64+0x100db2bde)
#7 0x10481bb2a in
clang::driver::Driver::BuildCompilation(llvm::ArrayRef<char const*>)
(clang-cl:x86_64+0x100dd0b2a)
#8 0x103a7ad2e in main (clang-cl:x86_64+0x10002fd2e)
#9 0x7fff7928b3d4 in start (libdyld.dylib:x86_64+0x163d4)
This is because Option.cpp does this for aliases:
const Option &UnaliasedOption = getUnaliasedOption();
StringRef Spelling;
// If the option was an alias, get the spelling from the unaliased one.
if (getID() == UnaliasedOption.getID()) {
/// ... unimportant ...
} else {
Spelling = Args.MakeArgString(Twine(UnaliasedOption.getPrefix()) +
Twine(UnaliasedOption.getName()));
}
Then, Driver.cpp does
// Parse any pass through args using default clang processing rather
// than clang-cl processing.
auto CLModePassThroughOptions = llvm::make_unique<InputArgList>(
ParseArgStrings(CLModePassThroughArgList, false, ContainsError));
if (!ContainsError)
for (auto *Opt : *CLModePassThroughOptions) {
appendOneArg(Opt, nullptr);
}
Here, the Spelling in the alias args are owned by CLModePassThroughOptions,
which soon goes out of scope -- and the ad-hoc copying logic in
auto appendOneArg = [&Args](const Arg *Opt, const Arg *BaseArg) {
unsigned Index = Args.MakeIndex(Opt->getSpelling());
Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Opt->getSpelling(),
Index, BaseArg);
Copy->getValues() = Opt->getValues();
if (Opt->isClaimed())
Copy->claim();
Args.append(Copy);
};
doesn't copy the Spelling storage from one ArgList to the other.</pre>
</div>
</p>
<hr>
<span>You are receiving this mail because:</span>
<ul>
<li>You are on the CC list for the bug.</li>
</ul>
</body>
</html>