json
json copied to clipboard
Memory leak when exception is thrown in adl_serializer::to_json
Description
Throwing an exception in serializer function after the output json object is already initialized results in a memory leak (detected with both ASan and valgrind):
void adl_serializer<Foo>::to_json(json& j, Foo const& f) {
j["a"] = 42;
throw std::runtime_error("error");
}
Reproduction steps
Reproducer here: https://godbolt.org/z/dvorMExWh
Expected vs. actual results
Expected: no memory leak
Minimal code example
No response
Error messages
No response
Compiler and operating system
linux, clang 15, gcc 12.2
Library version
3.11.2, godbolt trunk, 7f72eedc2d4fc196d389f5aa0b2659f70dabe278
Validation
- [X] The bug also occurs if the latest version from the
develop
branch is used. - [ ] I can successfully compile and run the unit tests.
This is throwing inside the constructor. When the constructor doesn't complete without exception, then the destructor is not run, meaning that m_value.destroy(m_type);
is not called. Cleaning up from this is going to be tricky.
https://isocpp.org/wiki/faq/exceptions#selfcleaning-members
Off the top of my head, the constructor code that calls out to the adl serializer could detect an exception and call m_value.destroy(m_type);
manually before rethrowing.
This can be solved by either using a base class or a member var to do the cleanup. I would prefer one of those solutions, since they solve the problem of leaking memory permanently.
The stl uses base classes for containers managing memory (https://github.com/gcc-mirror/gcc/blob/7c755fd9018821b79ddc32ee507897860510986c/libstdc%2B%2B-v3/include/bits/stl_vector.h#L423). If we use a base class, we do not need to change the variable names. It would be enough to move some code into this base class.
I will implement a solution using a base class in #3901.
/edit I went with the solution using a member var, since it requires less changes to the existing code and the resulting code is simpler to understand.