diff --git a/outside.go b/outside.go index 28be3520..a19f2bdb 100644 --- a/outside.go +++ b/outside.go @@ -231,7 +231,7 @@ func (f *Interface) handleOutsideRelayPacket(hostinfo *HostInfo, via ViaSender, case ForwardingType: // Forward this packet through the relay tunnel, rebuilding it in place. // Encode overwrites the old outer header, and the new AEAD tag lands where the old one was - fwdBuf := packet[:0:len(packet)] // Cap to len(packet) to protect memory from a larger parent buffer + fwdBuf := packet[:0] //todo it would potentially be nice to batch these f.SendVia(targetHI, targetRelay, signedPayload, nb, fwdBuf, true, q) case TerminalType: diff --git a/udp/udp_darwin.go b/udp/udp_darwin.go index b381920f..e64f1330 100644 --- a/udp/udp_darwin.go +++ b/udp/udp_darwin.go @@ -195,7 +195,7 @@ func (u *StdConn) ListenOut(r EncReader, flush func()) error { continue } - r(netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()), buffer[:n]) + r(netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()), buffer[:n:n]) flush() } } diff --git a/udp/udp_generic.go b/udp/udp_generic.go index 9735592c..119b5d7d 100644 --- a/udp/udp_generic.go +++ b/udp/udp_generic.go @@ -106,7 +106,7 @@ func (u *GenericConn) ListenOut(r EncReader, flush func()) error { continue } - r(netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()), buffer[:n]) + r(netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()), buffer[:n:n]) flush() } } diff --git a/udp/udp_linux.go b/udp/udp_linux.go index 001555a3..398baf3f 100644 --- a/udp/udp_linux.go +++ b/udp/udp_linux.go @@ -305,7 +305,7 @@ func (u *StdConn) ListenOut(r EncReader, flush func()) error { // deliverSegments hands a received superdatagram to r, splitting it back into pre-coalesce packets func deliverSegments(r EncReader, from netip.AddrPort, payload []byte, segSize int) { if segSize <= 0 || segSize >= len(payload) { //avoid bogus values - r(from, payload) + r(from, payload[:len(payload):len(payload)]) return } for off := 0; off < len(payload); off += segSize { @@ -313,7 +313,7 @@ func deliverSegments(r EncReader, from netip.AddrPort, payload []byte, segSize i if end > len(payload) { end = len(payload) } - r(from, payload[off:end]) + r(from, payload[off:end:end]) } } diff --git a/udp/udp_linux_fixes_test.go b/udp/udp_linux_fixes_test.go index 4ed3514d..414c5864 100644 --- a/udp/udp_linux_fixes_test.go +++ b/udp/udp_linux_fixes_test.go @@ -210,8 +210,10 @@ func TestParseRecvCmsgCorruptLenNoPanic(t *testing.T) { // error here shreds encrypted packets and every decrypt downstream fails. func TestDeliverSegments(t *testing.T) { from := netip.MustParseAddrPort("192.0.2.1:4242") + // Spare backing capacity mimics the recvmmsg row a real payload sits in; + // the cap checks below prove none of it leaks to a delivered segment. pay := func(n int) []byte { - b := make([]byte, n) + b := make([]byte, n, n+512) for i := range b { b[i] = byte(i) } @@ -262,6 +264,11 @@ func TestDeliverSegments(t *testing.T) { if len(seg) != wantLens[i] { t.Fatalf("segment %d len=%d want %d", i, len(seg), wantLens[i]) } + if cap(seg) != len(seg) { + // EncReader contract: an append into spare capacity would + // scribble into the next segment of the shared row. + t.Errorf("segment %d cap=%d, want %d (capacity must not reach into the row)", i, cap(seg), len(seg)) + } if len(seg) > 0 && &seg[0] != &c.payload[off] { t.Errorf("segment %d does not alias payload at offset %d", i, off) } diff --git a/udp/udp_rio_windows.go b/udp/udp_rio_windows.go index 566a44d9..b04770af 100644 --- a/udp/udp_rio_windows.go +++ b/udp/udp_rio_windows.go @@ -161,7 +161,7 @@ func (u *RIOConn) ListenOut(r EncReader, flush func()) error { continue } - r(netip.AddrPortFrom(netip.AddrFrom16(rua.Addr).Unmap(), (rua.Port>>8)|((rua.Port&0xff)<<8)), buffer[:n]) + r(netip.AddrPortFrom(netip.AddrFrom16(rua.Addr).Unmap(), (rua.Port>>8)|((rua.Port&0xff)<<8)), buffer[:n:n]) flush() } } diff --git a/udp/udp_tester.go b/udp/udp_tester.go index 13d0bc15..bb1a5afe 100644 --- a/udp/udp_tester.go +++ b/udp/udp_tester.go @@ -189,7 +189,7 @@ func (u *TesterConn) ListenOut(r EncReader, flush func()) error { case <-u.done: return os.ErrClosed case p := <-u.RxPackets: - r(p.From, p.Data) + r(p.From, p.Data[:len(p.Data):len(p.Data)]) // The batcher borrows plaintext decrypted in place inside p.Data // until Flush, so the packet must stay alive across flush() flush()