Skip to content

Own interp creation argv strings in InterpreterInfo#1049

Open
aaronj0 wants to merge 1 commit into
compiler-research:mainfrom
aaronj0:own-interp-argv-strings
Open

Own interp creation argv strings in InterpreterInfo#1049
aaronj0 wants to merge 1 commit into
compiler-research:mainfrom
aaronj0:own-interp-argv-strings

Conversation

@aaronj0

@aaronj0 aaronj0 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

CreateInterpreter built ClingArgv from pointers of function-local strings, currently the executable path, extracted resource dir, libc++ include dir, and any CPPINTEROP_EXTRA_INTERPRETER_ARGS. But the interpreter keeps the raw const char* pointers for its whole lifetime (cling::CompilerOptions and the analagous one for clang-repl), so asan reports accesses to them after CreateInterpreter returns:

[ RUN      ] CppInterOpTest/InProcessJIT.Interpreter_CodeCompletion
=================================================================
==1437237==ERROR: AddressSanitizer: heap-use-after-free on address 0x7be28500f1a0 at pc 0x7f7286089a68 bp 0x7fff6c078930 sp 0x7fff6c0780d8
READ of size 4 at 0x7be28500f1a0 thread T0
    #0 0x7f7286089a67  (/usr/lib/gcc/x86_64-pc-linux-gnu/16/libasan.so.8+0x89a67) (BuildId: 891183677d01506ed69deae224395d45682a5367)
    #1 0x558be1eef857 in clang::driver::getDriverMode(llvm::StringRef, llvm::ArrayRef<char const*>) (/home/stephan/root-asan-gcc/interpreter/CppInterOp/unittests/CppInterOp/bin/Debug/CppInterOpTests+0x6942857) (BuildId: da5edc44dacb04ca63d180e19d480fec90f37900)
    #2 0x558be1fe149b in clang::driver::Driver::BuildCompilation(llvm::ArrayRef<char const*>) (/home/stephan/root-asan-gcc/interpreter/CppInterOp/unittests/CppInterOp/bin/Debug/CppInterOpTests+0x6a3449b) (BuildId: da5edc44dacb04ca63d180e19d480fec90f37900)
    #3 0x558bdf28ac60 in createCIImpl /home/stephan/code/root/interpreter/cling/lib/Interpreter/CIFactory.cpp:1480
    #4 0x558bdf28ff8f in cling::CIFactory::createCI(llvm::StringRef, cling::InvocationOptions const&, char const*, std::optional<std::unique_ptr<clang::ASTConsumer, std::default_delete<clang::ASTConsumer> > >, std::vector<std::shared_ptr<clang::ModuleFileExtension>, std::allocator<std::shared_ptr<clang::ModuleFileExtension> > > const&, bool) /home/stephan/code/root/interpreter/cling/lib/Interpreter/CIFactory.cpp:1836
    #5 0x558bdf10f69a in cling::Interpreter::codeComplete(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&) const /home/stephan/code/root/interpreter/cling/lib/Interpreter/Interpreter.cpp:1042
    #6 0x558bdf08e2e3 in compat::codeComplete(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, cling::Interpreter const&, char const*, unsigned int, unsigned int) (/home/stephan/root-asan-gcc/interpreter/CppInterOp/unittests/CppInterOp/bin/Debug/CppInterOpTests+0x3ae12e3) (BuildId: da5edc44dacb04ca63d180e19d480fec90f37900)
    #7 0x558bdf00872d in CppImpl::CodeComplete(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, char const*, unsigned int, unsigned int) /home/stephan/code/root/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp:5118
    #8 0x558bdee2d170 in CppInterOpTest_Interpreter_CodeCompletion_Test<InProcessJITConfig>::TestBody() /home/stephan/code/root/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp:428
    #9 0x7f7285fe6496 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (/usr/lib64/libgtest.so.1.17.0+0x65496)
    #10 0x7f7285fd15e5 in testing::Test::Run() (/usr/lib64/libgtest.so.1.17.0+0x505e5)
    #11 0x7f7285fd1784 in testing::TestInfo::Run() (/usr/lib64/libgtest.so.1.17.0+0x50784)
    #12 0x7f7285fd1a8c in testing::TestSuite::Run() (/usr/lib64/libgtest.so.1.17.0+0x50a8c)
    #13 0x7f7285fdb35a in testing::internal::UnitTestImpl::RunAllTests() (/usr/lib64/libgtest.so.1.17.0+0x5a35a)
    #14 0x7f7285fe6be6 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (/usr/lib64/libgtest.so.1.17.0+0x65be6)
    #15 0x7f7285fd1b7f in testing::UnitTest::Run() (/usr/lib64/libgtest.so.1.17.0+0x50b7f)
    #16 0x7f7286751105 in main (/usr/lib64/libgtest_main.so.1.17.0+0x1105)
    #17 0x7f728586ba3d in __libc_start_call_main (/usr/lib64/libc.so.6+0x24a3d) (BuildId: 476ff2df69e855980cf0fbf6ca0cba3afe36fcbe)
    #18 0x7f728586bb63 in __libc_start_main_impl (/usr/lib64/libc.so.6+0x24b63) (BuildId: 476ff2df69e855980cf0fbf6ca0cba3afe36fcbe)
    #19 0x558bdeca3374 in _start (/home/stephan/root-asan-gcc/interpreter/CppInterOp/unittests/CppInterOp/bin/Debug/CppInterOpTests+0x36f6374) (BuildId: da5edc44dacb04ca63d180e19d480fec90f37900)

0x7be28500f1a0 is located 0 bytes inside of 71-byte region [0x7be28500f1a0,0x7be28500f1e7)
freed by thread T0 here:
    #0 0x7f728612cd8f in operator delete(void*, unsigned long) (/usr/lib/gcc/x86_64-pc-linux-gnu/16/libasan.so.8+0x12cd8f) (BuildId: 891183677d01506ed69deae224395d45682a5367)
    #1 0x558bdecb72c3 in std::__new_allocator<char>::deallocate(char*, unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/new_allocator.h:183
    #2 0x558bdecb72c3 in std::allocator<char>::deallocate(char*, unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/allocator.h:218
    #3 0x558bdecb72c3 in std::allocator_traits<std::allocator<char> >::deallocate(std::allocator<char>&, char*, unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/alloc_traits.h:689
    #4 0x558bdecb72c3 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_destroy(unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.h:304
    #5 0x558bdecb72c3 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose() /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.h:298
    #6 0x558bdf013ab7 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.h:920
    #7 0x558bdf013ab7 in CppImpl::CreateInterpreter(std::vector<char const*, std::allocator<char const*> > const&, std::vector<char const*, std::allocator<char const*> > const&) /home/stephan/code/root/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp:4195
    #8 0x558bdecc6927 in CppInterOpTest<InProcessJITConfig>::CreateInterpreter(std::vector<char const*, std::allocator<char const*> > const&, std::vector<char const*, std::allocator<char const*> > const&) /home/stephan/code/root/interpreter/CppInterOp/unittests/CppInterOp/Utils.h:104
    #9 0x558bdee2d057 in CppInterOpTest_Interpreter_CodeCompletion_Test<InProcessJITConfig>::TestBody() /home/stephan/code/root/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp:425
    #10 0x7f7285fe6496 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (/usr/lib64/libgtest.so.1.17.0+0x65496)
    #11 0x7f7285fd15e5 in testing::Test::Run() (/usr/lib64/libgtest.so.1.17.0+0x505e5)
    #12 0x7f7285fd1784 in testing::TestInfo::Run() (/usr/lib64/libgtest.so.1.17.0+0x50784)
    #13 0x7f7285fd1a8c in testing::TestSuite::Run() (/usr/lib64/libgtest.so.1.17.0+0x50a8c)
    #14 0x7f7285fdb35a in testing::internal::UnitTestImpl::RunAllTests() (/usr/lib64/libgtest.so.1.17.0+0x5a35a)
    #15 0x7f7285fe6be6 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (/usr/lib64/libgtest.so.1.17.0+0x65be6)
    #16 0x7f7285fd1b7f in testing::UnitTest::Run() (/usr/lib64/libgtest.so.1.17.0+0x50b7f)
    #17 0x7f7286751105 in main (/usr/lib64/libgtest_main.so.1.17.0+0x1105)
    #18 0x7f728586ba3d in __libc_start_call_main (/usr/lib64/libc.so.6+0x24a3d) (BuildId: 476ff2df69e855980cf0fbf6ca0cba3afe36fcbe)
    #19 0x7f728586bb63 in __libc_start_main_impl (/usr/lib64/libc.so.6+0x24b63) (BuildId: 476ff2df69e855980cf0fbf6ca0cba3afe36fcbe)
    #20 0x558bdeca3374 in _start (/home/stephan/root-asan-gcc/interpreter/CppInterOp/unittests/CppInterOp/bin/Debug/CppInterOpTests+0x36f6374) (BuildId: da5edc44dacb04ca63d180e19d480fec90f37900)

previously allocated by thread T0 here:
    #0 0x7f728612bc0f in operator new(unsigned long) (/usr/lib/gcc/x86_64-pc-linux-gnu/16/libasan.so.8+0x12bc0f) (BuildId: 891183677d01506ed69deae224395d45682a5367)
    #1 0x558bdecb7259 in std::__new_allocator<char>::allocate(unsigned long, void const*) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/new_allocator.h:162
    #2 0x558bdecb7259 in std::allocator<char>::allocate(unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/allocator.h:206
    #3 0x558bdecb7259 in std::allocator_traits<std::allocator<char> >::allocate(std::allocator<char>&, unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/alloc_traits.h:638
    #4 0x558bdecb7259 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_S_allocate(std::allocator<char>&, unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.h:141
    #5 0x558bdecb7259 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.tcc:164
    #6 0x558bdecb741e in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.tcc:235
    #7 0x558bdedf5ab0 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, unsigned long, std::allocator<char> const&) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.h:716
    #8 0x558bdedf5e3f in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::__sv_wrapper, std::allocator<char> const&) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.h:192
    #9 0x558bdedf5e3f in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<llvm::StringRef, void>(llvm::StringRef const&, std::allocator<char> const&) /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/g++-v16/bits/basic_string.h:912
    #10 0x558bdefdbefa in MakeResourcesPath /home/stephan/code/root/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp:4003
    #11 0x558bdf012040 in CppImpl::CreateInterpreter(std::vector<char const*, std::allocator<char const*> > const&, std::vector<char const*, std::allocator<char const*> > const&) /home/stephan/code/root/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp:4050
    #12 0x558bdecc6927 in CppInterOpTest<InProcessJITConfig>::CreateInterpreter(std::vector<char const*, std::allocator<char const*> > const&, std::vector<char const*, std::allocator<char const*> > const&) /home/stephan/code/root/interpreter/CppInterOp/unittests/CppInterOp/Utils.h:104
    #13 0x558bdee2d057 in CppInterOpTest_Interpreter_CodeCompletion_Test<InProcessJITConfig>::TestBody() /home/stephan/code/root/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp:425
    #14 0x7f7285fe6496 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (/usr/lib64/libgtest.so.1.17.0+0x65496)
    #15 0x7f7285fd15e5 in testing::Test::Run() (/usr/lib64/libgtest.so.1.17.0+0x505e5)
    #16 0x7f7285fd1784 in testing::TestInfo::Run() (/usr/lib64/libgtest.so.1.17.0+0x50784)
    #17 0x7f7285fd1a8c in testing::TestSuite::Run() (/usr/lib64/libgtest.so.1.17.0+0x50a8c)
    #18 0x7f7285fdb35a in testing::internal::UnitTestImpl::RunAllTests() (/usr/lib64/libgtest.so.1.17.0+0x5a35a)
    #19 0x7f7285fe6be6 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (/usr/lib64/libgtest.so.1.17.0+0x65be6)
    #20 0x7f7285fd1b7f in testing::UnitTest::Run() (/usr/lib64/libgtest.so.1.17.0+0x50b7f)
    #21 0x7f7286751105 in main (/usr/lib64/libgtest_main.so.1.17.0+0x1105)
    #22 0x7f728586ba3d in __libc_start_call_main (/usr/lib64/libc.so.6+0x24a3d) (BuildId: 476ff2df69e855980cf0fbf6ca0cba3afe36fcbe)
    #23 0x7f728586bb63 in __libc_start_main_impl (/usr/lib64/libc.so.6+0x24b63) (BuildId: 476ff2df69e855980cf0fbf6ca0cba3afe36fcbe)
    #24 0x558bdeca3374 in _start (/home/stephan/root-asan-gcc/interpreter/CppInterOp/unittests/CppInterOp/bin/Debug/CppInterOpTests+0x36f6374) (BuildId: da5edc44dacb04ca63d180e19d480fec90f37900)

SUMMARY: AddressSanitizer: heap-use-after-free (/home/stephan/root-asan-gcc/interpreter/CppInterOp/unittests/CppInterOp/bin/Debug/CppInterOpTests+0x6942857) (BuildId: da5edc44dacb04ca63d180e19d480fec90f37900) in clang::driver::getDriverMode(llvm::StringRef, llvm::ArrayRef<char const*>)
Shadow bytes around the buggy address:
  0x7be28500ef00: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fd fd
  0x7be28500ef80: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
  0x7be28500f000: fd fd fd fd fd fa fa fa fa fa fd fd fd fd fd fd
  0x7be28500f080: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
  0x7be28500f100: fd fd fa fa fa fa fd fd fd fd fd fd fd fd fd fa
=>0x7be28500f180: fa fa fa fa[fd]fd fd fd fd fd fd fd fd fa fa fa
  0x7be28500f200: fa fa 00 00 00 00 00 00 00 00 00 00 fa fa fa fa
  0x7be28500f280: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fd fd
  0x7be28500f300: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
  0x7be28500f380: fd fd fd fd fd fa fa fa fa fa fd fd fd fd fd fd
  0x7be28500f400: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb

This patch fixes this by collecting owned copies of every argument and store it as a part of per-interpreter info, so it is released when DeleteInterpreter is called

CreateInterpreter built ClingArgv from pointers of function-local strings
(main executable path, extracted resource dir, libc++ include dir,
CPPINTEROP_EXTRA_INTERPRETER_ARGS). But the interpreter keeps the raw argv
pointers for its whole lifetime (cling::CompilerOptions, analagous for
clang-repl), so asan reports accesses to them after CreateInterpreter returns.

Fix: Collect owned copies of every argument and store it as a part of
per-interpreter info, so it is released when DeleteInterpreter is called
@aaronj0 aaronj0 requested a review from vgvassilev July 3, 2026 14:45
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.23810% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 86.75%. Comparing base (e3fc693) to head (e9e48ce).

Files with missing lines Patch % Lines
lib/CppInterOp/CppInterOp.cpp 93.75% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1049      +/-   ##
==========================================
+ Coverage   86.73%   86.75%   +0.02%     
==========================================
  Files          23       23              
  Lines        5970     5973       +3     
==========================================
+ Hits         5178     5182       +4     
+ Misses        792      791       -1     
Files with missing lines Coverage Δ
lib/CppInterOp/InterpreterInfo.h 100.00% <100.00%> (ø)
lib/CppInterOp/CppInterOp.cpp 89.35% <93.75%> (+0.03%) ⬆️
Files with missing lines Coverage Δ
lib/CppInterOp/InterpreterInfo.h 100.00% <100.00%> (ø)
lib/CppInterOp/CppInterOp.cpp 89.35% <93.75%> (+0.03%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

ClingArgv.insert(ClingArgv.begin(), {"-resource-dir", ResourceDir.c_str()});
ClingArgv.insert(ClingArgv.begin(), MainExecutableName.c_str());
if (!ResourceDir.empty()) {
ArgvStorage.push_back("-resource-dir");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use emplace_back instead of push_back [modernize-use-emplace]

Suggested change
ArgvStorage.push_back("-resource-dir");
mpty()) {emplace_back(

ArgvStorage.push_back("-resource-dir");
ArgvStorage.push_back(ResourceDir);
}
ArgvStorage.push_back("-std=c++14");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use emplace_back instead of push_back [modernize-use-emplace]

Suggested change
ArgvStorage.push_back("-std=c++14");
Dir);emplace_back(

@vgvassilev

Copy link
Copy Markdown
Contributor

Why our infra did not catch this?

@aaronj0

aaronj0 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Why our infra did not catch this?

I don't know. Are we running asan in gcc or clang?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants