When pppd is spawned (in ppp_unix.c:ppp_unix_pppd_spawn:1062) l2tp_tunnel_session_remove (from the same plugin ppp_unix.so) is set as child termination cleanup callback. But during l2tp clean up procedure (l2tp_cleanup) all plugins are unloaded (l2tp_plugin_cleanup) before child process list is cleaned (usl_pid_cleanup), so when child cleanup callbacks for pppd are called ppp_unix.so is already unloaded (in my case those memory regions are not even mapped anymore) and SIGSEGV happily arises (and pid file is never unlinked). Patch attached doesn't change any cleanup sequence logic, it only breaks l2tp_plugin_cleanup into two parts:
l2tp_plugin_cleanup that calls "openl2tp_plugin_cleanup" from plugins
and l2tp_plugin_release that actually unloads shared objects and frees remaining resources
l2tp_plugin_release is called after usl_pid_cleanup.