Dangling std::string pointers in mesh class
The boundarylayer python test crash reproducibly. According to valgrind the mesh class contains quite some problematic code for the bcnames array. Typical valgrind report:
test_boundarylayer.py ==409999== Invalid read of size 8
==409999== at 0x9FF0ABB: UnknownInlinedFun (basic_string.h:228)
==409999== by 0x9FF0ABB: UnknownInlinedFun (basic_string.h:556)
==409999== by 0x9FF0ABB: netgen::Mesh::SetBCName(int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (meshclass.cpp:7333)
==409999== by 0x9F60529: netgen::BoundaryLayerTool::CreateFaceDescriptorsSides() (boundarylayer.cpp:725)
==409999== by 0x9F6337C: netgen::BoundaryLayerTool::Perform() (boundarylayer.cpp:1523)
==409999== by 0x9F6824F: netgen::GenerateBoundaryLayer(netgen::Mesh&, netgen::BoundaryLayerParameters const&) (boundarylayer.cpp:1552)
==409999== by 0xA33094B: UnknownInlinedFun (python_mesh.cpp:1502)
==409999== by 0xA33094B: void pybind11::detail::argument_loader...(cast.h:1631)
==409999== by 0xA098C90: UnknownInlinedFun (cast.h:1605)
==409999== by 0xA098C90: UnknownInlinedFun (pybind11.h:279)
==409999== by 0xA098C90: pybind11::cpp_function::initialize<ExportNetgenMeshing(pybind11::module_&)::...(pybind11.h:249)
==409999== by 0x9F39704: pybind11::cpp_function::dispatcher(_object*, _object*, _object*) (pybind11.h:971)
==409999== by 0x4A3346C: ??? (in /usr/lib64/libpython3.11.so.1.0)
==409999== by 0x4A1790E: _PyObject_MakeTpCall (in /usr/lib64/libpython3.11.so.1.0)
==409999== by 0x4A20272: _PyEval_EvalFrameDefault (in /usr/lib64/libpython3.11.so.1.0)
==409999== by 0x4A1C69E: ??? (in /usr/lib64/libpython3.11.so.1.0)
==409999== by 0x4A4492A: ??? (in /usr/lib64/libpython3.11.so.1.0)
==409999== Address 0x6f9c9e0 is 512 bytes inside a block of size 1,800 free'd
==409999== at 0x484C579: operator delete[](void*, unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==409999== by 0x9F5E0BA: UnknownInlinedFun (array.hpp:1136)
==409999== by 0x9F5E0BA: UnknownInlinedFun (array.hpp:883)
==409999== by 0x9F5E0BA: netgen::Mesh::AddFaceDescriptor(netgen::FaceDescriptor const&) (meshclass.hpp:699)
==409999== by 0x9F60506: netgen::BoundaryLayerTool::CreateFaceDescriptorsSides() (boundarylayer.cpp:723)
==409999== by 0x9F6337C: netgen::BoundaryLayerTool::Perform() (boundarylayer.cpp:1523)
==409999== by 0x9F6824F: netgen::GenerateBoundaryLayer(netgen::Mesh&, netgen::BoundaryLayerParameters const&) (boundarylayer.cpp:1552)
==409999== by 0xA33094B: UnknownInlinedFun (python_mesh.cpp:1502)
==409999== by 0xA33094B: void pybind11::detail::argument_loader<netgen::Mesh&, std::variant... (cast.h:1631)
==409999== by 0xA098C90: UnknownInlinedFun (cast.h:1605)
==409999== by 0xA098C90: UnknownInlinedFun (pybind11.h:279)
==409999== by 0xA098C90: pybind11::cpp_function::initialize<ExportNetgenMeshing(pybind11::module_&)::...(pybind11.h:249)
==409999== by 0x9F39704: pybind11::cpp_function::dispatcher(_object*, _object*, _object*) (pybind11.h:971)
==409999== by 0x4A3346C: ??? (in /usr/lib64/libpython3.11.so.1.0)
==409999== by 0x4A1790E: _PyObject_MakeTpCall (in /usr/lib64/libpython3.11.so.1.0)
==409999== by 0x4A20272: _PyEval_EvalFrameDefault (in /usr/lib64/libpython3.11.so.1.0)
==409999== by 0x4A1C69E: ??? (in /usr/lib64/libpython3.11.so.1.0)
==409999== Block was alloc'd at
==409999== at 0x484851F: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==409999== by 0x9F5DF2E: UnknownInlinedFun (array.hpp:1120)
==409999== by 0x9F5DF2E: UnknownInlinedFun (array.hpp:883)
==409999== by 0x9F5DF2E: netgen::Mesh::AddFaceDescriptor(netgen::FaceDescriptor const&) (meshclass.hpp:699)
==409999== by 0x9F60124: netgen::BoundaryLayerTool::CreateNewFaceDescriptors() (boundarylayer.cpp:673)
==409999== by 0x9F6336C: netgen::BoundaryLayerTool::Perform() (boundarylayer.cpp:1521)
==409999== by 0x9F6824F: netgen::GenerateBoundaryLayer(netgen::Mesh&, netgen::BoundaryLayerParameters const&) (boundarylayer.cpp:1552)
As can be seen the problematic memory block is from Array<FaceDecoding> Mesh::facedecoding, which has been grown (reallocated) by Mesh::AddFaceDescriptor. The FaceDescriptor::bcname is a member, thus growing the facedecoding array invalidates any pointers to the bcname.
Unfortunately, Mesh::operator=(const Mesh& mesh2) ignores this, and assigns the raw pointer &facedecoding[fi].bcname to bcnames:
https://github.com/NGSolve/netgen/blob/3bfa6c19fa8964700c08c0b92c99558523671761/libsrc/meshing/meshclass.cpp#L324-L349
Analysis for this case is not fully correct, the problem found by valgrind comes from some invalid code in BoundaryLayerTool::CreateFaceDescriptorSides():
https://github.com/NGSolve/netgen/blob/975414c2fe6dc44f290db822b09f96aac8ad29b6/libsrc/meshing/boundarylayer.cpp#L716-L728
mesh.AddFaceDescriptor(new_fd); may reallocate and thus invalidate fd. fd has to be refetched.