conn_linux: copy pooled receive buffers before parsing#284
Conversation
When MessageBufferSize is set, ReceiveIter parses messages from a pooled buffer and then returns that buffer to the pool. Message.UnmarshalBinary keeps Message.Data as a slice into the input buffer, so later receives can overwrite previously returned message payloads. Fix this by parsing from owned memory in the pooled path: copy the received bytes into a fresh aligned buffer before parseMessagesIter. The non-pooled path is unchanged and still avoids the extra copy. Add an integration regression test to verify that payload from the first Receive is not modified by a subsequent Receive when MessageBufferSize is enabled.
|
Thanks for this PR @ivanlevitsky ! I had a quick look and I think this looks good. I am wondering whether another alternative could be to copy into |
Thank you, this is a very reasonable alternative. I did consider copying in
So I’d prefer to keep this targeted fix. If you’d like, we can discuss changing |
|
Thanks for the detailed explanation!
Yes, do let me know if you think there are ways it can be improved. |
Problem
On Linux, when
Config.MessageBufferSizeis enabled, receive buffers are reused from a pool.Message.UnmarshalBinarykeepsMessage.Dataas a slice into the parsed input buffer, so returned messages may reference pooled memory that is reused by later receives.This is especially visible when responses are read across multiple
recvmsgcalls (for example, multipart netlink dumps): a later receive can overwrite data from previously returned messages.Root cause
ReceiveIterparsed messages directly from the pooled receive buffer and then returned that buffer to the pool before callers were necessarily done usingMessage.Data.Fix
ownedBufferinconn_linux.goand parse from it inReceiveIter.b[:nlmsgAlign(n)]) with no extra copy.nreceived bytes into a fresh aligned buffer and parse from that owned memory.This keeps the change minimal and targeted to the pooled-buffer path only.
Tests
Add integration regression test:
TestIntegrationConnMessageBufferSizeCopiesMessageDatainconn_linux_integration_test.go.NETLINK_USERSOCKwithMessageBufferSizeenabled.Receive()remains unchanged after the secondReceive().