contrib: fix unaligned&aliased levin buffer reads
What changed, and why it matters
This patch fixes the way Monero's network code reads incoming message headers. Previously, the code directly cast raw byte buffers to structured header types, which can crash or behave incorrectly on some processors when the memory address is not properly aligned. The fix copies the bytes into a properly aligned variable first using memcpy. This is a robustness/correctness fix that may prevent denial-of-service crashes from malformed or specially positioned network data, but the commit message does not frame it as a security vulnerability.
Treat as a stability and hardening fix; backport to release branches because malformed P2P traffic could crash nodes on strict-alignment architectures. Monitor for related follow-up fixes or a vendor advisory.
Security signals we found
Replaced unsafe pointer casts from network input buffer to typed structures with memcpy
Addresses undefined behavior from potentially unaligned/aliased reads
Could prevent remote denial-of-service crashes on strict-alignment platforms
No explicit security framing or CVE in commit message
Evidence from the diff
In contrib/epee/include/net/levin_protocol_handler_async.h, the levin protocol handler was dereferencing pointers cast directly from a span of cached input bytes to uint64_t and bucket_head2. This is undefined behavior when the underlying buffer is not suitably aligned, and on strict-alignment architectures (e.g., ARM, SPARC, RISC-V without unaligned support) can cause a bus fault or crash. The patch replaces the direct pointer dereferences with memcpy into local/ member variables (levin_sig and m_current_head), which is the standard portable way to deserialize unaligned data. The byte-swap logic is preserved, only the load path is changed. There is no evidence in the commit of an exploit for code execution or memory disclosure; the primary risk is a remote denial-of-service via a malformed connection that triggers an alignment fault.
Changed components
contrib/epee/include/net/levin_protocol_handler_async.hMonero P2P/network levin protocol handlerInspect captured patch +17 / −15
diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h
index 9e410f5..6634f79 100644
--- a/contrib/epee/include/net/levin_protocol_handler_async.h
+++ b/contrib/epee/include/net/levin_protocol_handler_async.h
@@ -550,32 +550,34 @@ public:
{
if(m_cache_in_buffer.size() < sizeof(bucket_head2))
{
- if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.span(8).data()) != SWAP64LE(LEVIN_SIGNATURE))
+ if(m_cache_in_buffer.size() >= sizeof(uint64_t))
{
- MWARNING(m_connection_context << "Signature mismatch, connection will be closed");
- return false;
+ std::uint64_t levin_sig;
+ memcpy(&levin_sig, m_cache_in_buffer.span(sizeof(std::uint64_t)).data(), sizeof(std::uint64_t));
+ if (SWAP64LE(LEVIN_SIGNATURE) != levin_sig)
+ {
+ MWARNING(m_connection_context << "Signature mismatch, connection will be closed");
+ return false;
+ }
}
is_continue = false;
break;
}
-#if BYTE_ORDER == LITTLE_ENDIAN
- bucket_head2& phead = *(bucket_head2*)m_cache_in_buffer.span(sizeof(bucket_head2)).data();
-#else
- bucket_head2 phead = *(bucket_head2*)m_cache_in_buffer.span(sizeof(bucket_head2)).data();
- phead.m_signature = SWAP64LE(phead.m_signature);
- phead.m_cb = SWAP64LE(phead.m_cb);
- phead.m_command = SWAP32LE(phead.m_command);
- phead.m_return_code = SWAP32LE(phead.m_return_code);
- phead.m_flags = SWAP32LE(phead.m_flags);
- phead.m_protocol_version = SWAP32LE(phead.m_protocol_version);
+ memcpy(&m_current_head, m_cache_in_buffer.span(sizeof(bucket_head2)).data(), sizeof(bucket_head2));
+#if BYTE_ORDER != LITTLE_ENDIAN
+ m_current_head.m_signature = SWAP64LE(m_current_head.m_signature);
+ m_current_head.m_cb = SWAP64LE(m_current_head.m_cb);
+ m_current_head.m_command = SWAP32LE(m_current_head.m_command);
+ m_current_head.m_return_code = SWAP32LE(m_current_head.m_return_code);
+ m_current_head.m_flags = SWAP32LE(m_current_head.m_flags);
+ m_current_head.m_protocol_version = SWAP32LE(m_current_head.m_protocol_version);
#endif
- if(LEVIN_SIGNATURE != phead.m_signature)
+ if(LEVIN_SIGNATURE != m_current_head.m_signature)
{
LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed");
return false;
}
- m_current_head = phead;
m_cache_in_buffer.erase(sizeof(bucket_head2));
m_state = stream_state_body;
Why this scored 61/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.