From acbd3f897bbd61831a2c18f02f3def9d45b92fb3 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 26 May 2024 15:33:37 -0400 Subject: [PATCH] fixed race condition between local buffer creation and sending it through the transports --- libi2pd/NTCP2.cpp | 10 ++++++---- libi2pd/NetDb.hpp | 6 +++++- libi2pd/RouterContext.cpp | 13 +++++++++++-- libi2pd/RouterContext.h | 4 +++- libi2pd/RouterInfo.cpp | 6 ++++++ libi2pd/RouterInfo.h | 4 +++- libi2pd/SSU2Session.cpp | 25 ++++++++++++++++--------- libi2pd/SSU2Session.h | 1 + 8 files changed, 51 insertions(+), 18 deletions(-) diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 58a1c539..2f2c92b1 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -129,7 +129,8 @@ namespace transport options[1] = 2; // ver htobe16buf (options + 2, paddingLength); // padLen // m3p2Len - auto bufLen = i2p::context.GetRouterInfo ().GetBufferLen (); + auto riBuffer = i2p::context.CopyRouterInfoBuffer (); + auto bufLen = riBuffer->GetBufferLen (); m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options htobe16buf (options + 4, m3p2Len); // fill m3p2 payload (RouterInfo block) @@ -138,7 +139,7 @@ namespace transport m3p2[0] = eNTCP2BlkRouterInfo; // block htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI m3p2[3] = 0; // flag - memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex + memcpy (m3p2 + 4, riBuffer->data (), bufLen); // TODO: eliminate extra copy // 2 bytes reserved htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds // 4 bytes reserved @@ -1200,7 +1201,8 @@ namespace transport void NTCP2Session::SendRouterInfo () { if (!IsEstablished ()) return; - auto riLen = i2p::context.GetRouterInfo ().GetBufferLen (); + auto riBuffer = i2p::context.CopyRouterInfoBuffer (); + auto riLen = riBuffer->GetBufferLen (); size_t payloadLen = riLen + 3 + 1 + 7; // 3 bytes block header + 1 byte RI flag + 7 bytes DateTime m_NextSendBuffer = new uint8_t[payloadLen + 16 + 2 + 64]; // up to 64 bytes padding // DateTime block @@ -1211,7 +1213,7 @@ namespace transport m_NextSendBuffer[9] = eNTCP2BlkRouterInfo; htobe16buf (m_NextSendBuffer + 10, riLen + 1); // size m_NextSendBuffer[12] = 0; // flag - memcpy (m_NextSendBuffer + 13, i2p::context.GetRouterInfo ().GetBuffer (), riLen); + memcpy (m_NextSendBuffer + 13, riBuffer->data (), riLen); // TODO: eliminate extra copy // padding block auto paddingSize = CreatePaddingBlock (payloadLen, m_NextSendBuffer + 2 + payloadLen, 64); payloadLen += paddingSize; diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 5b017dc6..7fe0e0d5 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -119,7 +119,11 @@ namespace data size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n); void ClearRouterInfos () { m_RouterInfos.clear (); }; - std::shared_ptr NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); }; + template + std::shared_ptr NewRouterInfoBuffer (TArgs&&... args) + { + return m_RouterInfoBuffersPool.AcquireSharedMt (std::forward(args)...); + } bool PopulateRouterInfoBuffer (std::shared_ptr r); std::shared_ptr NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); }; boost::shared_ptr NewRouterInfoAddresses () diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index d2ad89b7..b14baba9 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -77,7 +77,13 @@ namespace i2p m_Service->Stop (); } } - + + std::shared_ptr RouterContext::CopyRouterInfoBuffer () const + { + std::lock_guard l(m_RouterInfoMutex); + return m_RouterInfo.CopyBuffer (); + } + void RouterContext::CreateNewRouter () { m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, @@ -246,7 +252,10 @@ namespace i2p void RouterContext::UpdateRouterInfo () { - m_RouterInfo.CreateBuffer (m_Keys); + { + std::lock_guard l(m_RouterInfoMutex); + m_RouterInfo.CreateBuffer (m_Keys); + } m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO)); m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 2a1f4ef6..bc43d6a8 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -114,7 +114,8 @@ namespace garlic return std::shared_ptr (this, [](i2p::garlic::GarlicDestination *) {}); } - + std::shared_ptr CopyRouterInfoBuffer () const; + const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; }; const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; @@ -258,6 +259,7 @@ namespace garlic std::unordered_set m_PublishExcluded; uint32_t m_PublishReplyToken; bool m_IsHiddenMode; // not publish + mutable std::mutex m_RouterInfoMutex; }; extern RouterContext context; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 5fbb7026..13fd2e17 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -1120,6 +1120,12 @@ namespace data m_Buffer->SetBufferLen (len); } + std::shared_ptr RouterInfo::CopyBuffer () const + { + if (!m_Buffer) return nullptr; + return netdb.NewRouterInfoBuffer (*m_Buffer); + } + std::shared_ptr RouterInfo::NewBuffer () const { return netdb.NewRouterInfoBuffer (); diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index ec7644d7..412c815e 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -188,6 +188,7 @@ namespace data Buffer () = default; Buffer (const uint8_t * buf, size_t len); + Buffer (const Buffer& other): Buffer (other.data (), other.m_BufferLen) {}; size_t GetBufferLen () const { return m_BufferLen; }; void SetBufferLen (size_t len) { m_BufferLen = len; }; @@ -281,7 +282,8 @@ namespace data const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; }; void DeleteBuffer () { m_Buffer = nullptr; }; - std::shared_ptr GetSharedBuffer () const { return m_Buffer; }; + std::shared_ptr GetSharedBuffer () const { return m_Buffer; }; + std::shared_ptr CopyBuffer () const; bool IsUpdated () const { return m_IsUpdated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; }; diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index fa888e77..ff23f0d7 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -337,7 +337,7 @@ namespace transport { if (!s->IsEstablished ()) return; uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.GetSharedRouterInfo ()); + size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.CopyRouterInfoBuffer ()); if (payloadSize) { if (payloadSize < s->m_MaxPayloadSize) @@ -883,12 +883,12 @@ namespace transport // payload size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1 uint8_t * payload = m_SentHandshakePacket->payload; - size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); + size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ()); if (!payloadSize) { // split by two fragments maxPayloadSize += m_MaxPayloadSize; - payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); + payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ()); header.h.flags[0] = 0x02; // frag 0, total fragments 2 // TODO: check if we need more fragments } @@ -2537,27 +2537,34 @@ namespace transport size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr r) { - if (!r || !r->GetBuffer () || len < 5) return 0; + if (!r || len < 5) return 0; + return CreateRouterInfoBlock (buf, len, r->GetSharedBuffer ()); + } + + size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr riBuffer) + { + if (!riBuffer || len < 5) return 0; buf[0] = eSSU2BlkRouterInfo; - size_t size = r->GetBufferLen (); + size_t size = riBuffer->GetBufferLen (); if (size + 5 < len) { - memcpy (buf + 5, r->GetBuffer (), size); + memcpy (buf + 5, riBuffer->data (), size); buf[3] = 0; // flag } else { i2p::data::GzipDeflator deflator; deflator.SetCompressionLevel (9); - size = deflator.Deflate (r->GetBuffer (), r->GetBufferLen (), buf + 5, len - 5); + size = deflator.Deflate (riBuffer->data (), riBuffer->GetBufferLen (), buf + 5, len - 5); if (!size) return 0; // doesn't fit buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag } htobe16buf (buf + 1, size + 2); // size buf[4] = 1; // frag return size + 5; - } - + } + + size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len) { if (len < 8) return 0; diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index 4dfc529a..e4744b02 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -326,6 +326,7 @@ namespace transport size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr r); + size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr riBuffer); size_t CreateAckBlock (uint8_t * buf, size_t len); size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr&& msg);