-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory leak in std::string comparison after libc++ aaef3b82f4f0 #60709
Comments
It causes mysterious memory leaks when comparing std::string, see GitHub Issue #60709 and the code review. > All supported compilers support `consteval`, so there is no more need for the macro. > > Reviewed By: ldionne, Mordante, #libc > > Spies: libcxx-commits > > Differential Revision: https://reviews.llvm.org/D143489 This reverts commit aaef3b8.
I'm pretty sure this is a compiler problem. Changing |
@llvm/issue-subscribers-clang-frontend |
It's also worth noting that Clang doesn't currently define |
Sorry, I meant the other way around. Making the function
I am aware of that. The memory leak is still very surprising. |
Oh yeah that IS surprising!
Agreed, and I'm not suggesting this isn't necessarily a frontend issue in Clang. |
Removing the libc++ tag since this was fixed in libc++ by using |
I wonder if that could be related to #46711 - IE, code gen bug |
Still reproducible with trunk and the diff diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h
index c348f0433a32..e0104c018879 100644
--- a/libcxx/include/__compare/ordering.h
+++ b/libcxx/include/__compare/ordering.h
@@ -40,7 +40,7 @@ template<class _Tp, class... _Args>
inline constexpr bool __one_of_v = (is_same_v<_Tp, _Args> || ...);
struct _CmpUnspecifiedParam {
- _LIBCPP_HIDE_FROM_ABI constexpr
+ _LIBCPP_HIDE_FROM_ABI consteval
_CmpUnspecifiedParam(int _CmpUnspecifiedParam::*) noexcept {}
template<class _Tp, class = enable_if_t<!__one_of_v<_Tp, int, partial_ordering, weak_ordering, strong_ordering>>> I got the exact same error.
|
Reduced: struct _CmpUnspecifiedParam {
consteval _CmpUnspecifiedParam(int _CmpUnspecifiedParam::*) noexcept {} // Change here to constexpr to make leak go away
};
struct ordering {
int value;
friend constexpr bool operator<(ordering lhs, _CmpUnspecifiedParam) { return lhs.value > 0; }
};
class string {
public:
string(const char* data, unsigned int size) {Data = new char[size];}
friend ordering operator<=>(string lhs, const char* rhs) { return ordering{1}; }
~string() { delete[] Data;}
private:
char *Data;
};
struct S {
string ToString() { return string(data, sizeof(data)); }
char data[10] = {'a'};
};
int main() {
S s;
int i = 0;
do {
++i;
} while (i < 2 && s.ToString() < "whatev");
return 0;
} It seems changing constructor of
Looking further. |
The problematic bit seems to be this: llvm-project/clang/lib/Sema/SemaExpr.cpp Line 18173 in e829eb9
I'm not quite sure why immediate invocation would need a cleanup, but calling |
CC @zygoloid for any insights he might have based on the information you gathered so far |
For the record, I'm also looking into it. I can confirm @Fznamznon's conclusions and I also think the line mentioned above is probably wrong. I would assume that immediate invocations never need to run the cleanups themselves (there are no Here are a few more details. A smaller repro is: struct iparam {
consteval iparam() {}
};
struct string {
string(int v) { this->data = new int(v); }
~string() { delete data; }
private:
int *data;
};
int main() {
for (;string(1), iparam(), false;);
return 0;
} See godbolt.
If constructor is not
Note that for (;iparam(), string(1), false;); Or if the temporary is created both before and after the immediate invocation: for (;string(1), iparam(), string(1), false;); In this case, we get actually get two
|
I also have another guess, that even though the line I mentioned above may not be correct, it should bring no harm. I suspect that @ilya-biryukov given that your findings confirm my earlier conclusion, what about I post a patch removing the problematic bit? |
IIUC the struct param {
consteval param() { val = nullptr; allocated = false; }
~param() { delete val; }
param& assign(int v) {
if (!allocated) {
val = new int;
allocated = true;
}
*val = v;
return *this;
}
int get() { return *val; }
private:
int *val;
bool allocated;
};
int main() {
// param() is an immediate invocation,
// but we need to run the destructor for a temporary produced by it.
for (; param().assign(10).get() < 5;) {}
}
@Fznamznon this sounds good, let's try this out. I think it would also be valuable to add tests that check AST and codegen is doing the right thing here. We could continue the discussion in Phabricator. |
I've created https://reviews.llvm.org/D153294
I see! Thank you for the explanation. |
When sanitizer is ON, unittest_rgw_lua shows ``` ================================================================= ==3738104==ERROR: LeakSanitizer: detected memory leaks Direct leak of 31 byte(s) in 1 object(s) allocated from: #0 0xaaaac100e848 in operator new(unsigned long) (/root/ceph/build/bin/unittest_rgw_lua+0x25fe848) (BuildId: 524cddb1d44130431ac70e09896af3ab7cecef82) #1 0xffff9356dec0 in __gnu_cxx::new_allocator<char>::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/ext/new_allocator.h:127:27 #2 0xffff9356de3c in std::allocator<char>::allocate(unsigned long) /usr/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/bits/allocator.h:185:32 #3 0xffff9356de3c in std::allocator_traits<std::allocator<char> >::allocate(std::allocator<char>&, unsigned long) /usr/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/bits/alloc_traits.h:464:20 #4 0xffff9356db3c in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long) /usr/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.tcc:153:14 #5 0xffff93570bb0 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/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.tcc:219:14 #6 0xffff935e1bbc in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct_aux<char const*>(char const*, char const*, std::__false_type) /usr/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:255:11 ceph#7 0xffff935e197c in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*) /usr/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:274:4 ceph#8 0xffff935da484 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, unsigned long, std::allocator<char> const&) /usr/bin/../lib/gcc/aarch64-linux-gnu/11/../../../../include/c++/11/bits/basic_string.h:521:9 ceph#9 0xffff95b3d0ac in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > fmt::v9::to_string<char, 500ul>(fmt::v9::basic_memory_buffer<char, 500ul, std::allocator<char> > const&) /root/ceph/src/fmt/include/fmt/format.h:4050:10 ceph#10 0xffff95b39874 in fmt::v9::vformat[abi:cxx11](fmt::v9::basic_string_view<char>, fmt::v9::basic_format_args<fmt::v9::basic_format_context<fmt::v9::appender, char> >) /root/ceph/src/fmt/include/fmt/format-inl.h:1473:10 ceph#11 0xaaaac1264ab4 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > fmt::v9::format<std::basic_string_view<char, std::char_traits<char> > const&>(fmt::v9::basic_format_string<char, fmt::v9::type_identity<std::basic_string_view<char, std::char_traits<char> > const&>::type>, std::basic_string_view<char, std::char_traits<char> > const&) /root/ceph/src/fmt/include/fmt/core.h:3206:10 ceph#12 0xaaaac1264ab4 in rgw::lua::get_iterator_name[abi:cxx11](std::basic_string_view<char, std::char_traits<char> >) /root/ceph/src/rgw/rgw_lua_utils.h:276:10 ceph#13 0xaaaac1286864 in boost::container::flat_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, void>::iterator* rgw::lua::create_iterator_metadata<boost::container::flat_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, void> >(lua_State*, std::basic_string_view<char, std::char_traits<char> >, boost::container::flat_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, void>::iterator const&, boost::container::flat_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, void>::iterator const&) /root/ceph/src/rgw/rgw_lua_utils.h:295:38 ceph#14 0xaaaac128603c in int rgw::lua::next<boost::container::flat_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, void>, void>(lua_State*) /root/ceph/src/rgw/rgw_lua_utils.h:432:15 ceph#15 0xffff917d1e94 (/lib/aarch64-linux-gnu/liblua5.3.so.0+0x11e94) (BuildId: 3debb95525f7191c93f5ba6001de5c986b4cedfb) ceph#16 0xffff917d20ec (/lib/aarch64-linux-gnu/liblua5.3.so.0+0x120ec) (BuildId: 3debb95525f7191c93f5ba6001de5c986b4cedfb) ceph#17 0xffff917dc32c (/lib/aarch64-linux-gnu/liblua5.3.so.0+0x1c32c) (BuildId: 3debb95525f7191c93f5ba6001de5c986b4cedfb) ceph#18 0xffff917d23b8 (/lib/aarch64-linux-gnu/liblua5.3.so.0+0x123b8) (BuildId: 3debb95525f7191c93f5ba6001de5c986b4cedfb) ceph#19 0xffff917ca528 (/lib/aarch64-linux-gnu/liblua5.3.so.0+0xa528) (BuildId: 3debb95525f7191c93f5ba6001de5c986b4cedfb) ceph#20 0xffff917ccf38 (/lib/aarch64-linux-gnu/liblua5.3.so.0+0xcf38) (BuildId: 3debb95525f7191c93f5ba6001de5c986b4cedfb) ceph#21 0xffff917d226c in lua_pcallk (/lib/aarch64-linux-gnu/liblua5.3.so.0+0x1226c) (BuildId: 3debb95525f7191c93f5ba6001de5c986b4cedfb) ceph#22 0xaaaac1232a8c in rgw::lua::request::execute(rgw::sal::Driver*, RGWREST*, OpsLogSink*, req_state*, RGWOp*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /root/ceph/src/rgw/rgw_lua_request.cc:823:9 ceph#23 0xaaaac1021934 in TestRGWLua_MetadataIterator_Test::TestBody() /root/ceph/src/test/rgw/test_rgw_lua.cc:628:8 ceph#24 0xaaaac121a40c in void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /root/ceph/src/googletest/googletest/src/gtest.cc:2605:10 ceph#25 0xaaaac11cee0c in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /root/ceph/src/googletest/googletest/src/gtest.cc:2641:14 ceph#26 0xaaaac1182268 in testing::Test::Run() /root/ceph/src/googletest/googletest/src/gtest.cc:2680:5 ceph#27 0xaaaac11841ac in testing::TestInfo::Run() /root/ceph/src/googletest/googletest/src/gtest.cc:2858:11 ceph#28 0xaaaac11857ac in testing::TestSuite::Run() /root/ceph/src/googletest/googletest/src/gtest.cc:3012:28 ceph#29 0xaaaac11a1570 in testing::internal::UnitTestImpl::RunAllTests() /root/ceph/src/googletest/googletest/src/gtest.cc:5723:44 ceph#30 0xaaaac1224280 in bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /root/ceph/src/googletest/googletest/src/gtest.cc:2605:10 ceph#31 0xaaaac11d593c in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /root/ceph/src/googletest/googletest/src/gtest.cc:2641:14 SUMMARY: AddressSanitizer: 31 byte(s) leaked in 1 allocation(s). ``` Should avoid std::string does not be freed. https://github.com/ceph/ceph/blob/08d35a8d8529783882dd092c73c0b27be41c4d86/src/rgw/rgw_lua_utils.h#L364, this way should be OK. Reported issue: llvm/llvm-project#60709 Fix: llvm/llvm-project@c6b12b7 (clang >= 17, but CI use clang 14) Signed-off-by: Rongqi Sun <sunrongqi@huawei.com>
Since an immediate invocation is a full expression itself - it requires an additional ExprWithCleanups node, but it can participate to a bigger full expression which actually requires cleanups to be run after. Thanks @ilya-biryukov for helping reducing the reproducer and confirming that the analysis is correct. Fixes llvm/llvm-project#60709 Reviewed By: ilya-biryukov Differential Revision: https://reviews.llvm.org/D153962
It's mysterious, but bisection clearly points to aaef3b8. Here is a reduced reproducer:
Dropping the
-std=c++20
flag makes the leak go away.The diff below also makes it go away.
The text was updated successfully, but these errors were encountered: