diff --git a/include/rtpmidid/rtppeer.hpp b/include/rtpmidid/rtppeer.hpp index 86741dd..e781405 100644 --- a/include/rtpmidid/rtppeer.hpp +++ b/include/rtpmidid/rtppeer.hpp @@ -23,6 +23,7 @@ #include "signal.hpp" #include "stats.hpp" #include +#include #include namespace rtpmidid { @@ -41,7 +42,7 @@ class bad_midi_packet : public ::rtpmidid::exception { : ::rtpmidid::exception("Bad MIDI packet: {}", what) {} }; -class rtppeer_t { +class rtppeer_t : public std::enable_shared_from_this { NON_COPYABLE_NOR_MOVABLE(rtppeer_t) public: // Commands, the id is the same chars as the name diff --git a/lib/rtppeer.cpp b/lib/rtppeer.cpp index 56cf80d..a72b1f4 100644 --- a/lib/rtppeer.cpp +++ b/lib/rtppeer.cpp @@ -858,6 +858,17 @@ void rtppeer_t::parse_journal_chapter_N(uint8_t channel, } } void rtppeer_t::disconnect() { + // Keep ourselves alive across send_goodbye(): it fires status_change_event, + // whose rtpserverpeer slot calls rtpserver_t::remove_peer(), which erases + // the owning rtpserverpeer_t from a vector and drops the last + // shared_ptr. Without this local, the reset() below would + // run on freed memory. + // + // weak_from_this().lock() returns nullptr when this instance isn't owned + // by a shared_ptr (rtpclient_t embeds rtppeer_t by value; tests construct + // it on the stack). In that case no external owner exists to drop us + // mid-call, so the UAF can't occur and proceeding without self is safe. + auto self = weak_from_this().lock(); if (status & MIDI_CONNECTED) { send_goodbye(MIDI_PORT); }