Skip to content

Commit dbb6597

Browse files
committed
wgengine/netstack: add a missing refcount decrement after packet injection
Fixes tailscale#3762 Updates tailscale#3745 (probably fixes?) Change-Id: I1d3f0590fd5b8adfbc9110bc45ff717bb9e79aae Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> (cherry picked from commit 185825d)
1 parent 2e80227 commit dbb6597

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

wgengine/netstack/netstack.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ func (ns *Impl) injectInbound(p *packet.Parsed, t *tstun.Wrapper) filter.Respons
520520
Data: vv,
521521
})
522522
ns.linkEP.InjectInbound(pn, packetBuf)
523+
packetBuf.DecRef()
523524

524525
// We've now delivered this to netstack, so we're done.
525526
// Instead of returning a filter.Accept here (which would also

wgengine/netstack/netstack_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package netstack
6+
7+
import (
8+
"runtime"
9+
"testing"
10+
11+
"inet.af/netaddr"
12+
"tailscale.com/net/packet"
13+
"tailscale.com/net/tsdial"
14+
"tailscale.com/net/tstun"
15+
"tailscale.com/wgengine"
16+
"tailscale.com/wgengine/filter"
17+
)
18+
19+
// TestInjectInboundLeak tests that injectInbound doesn't leak memory.
20+
// See https://github.com/tailscale/tailscale/issues/3762
21+
func TestInjectInboundLeak(t *testing.T) {
22+
tunDev := tstun.NewFake()
23+
dialer := new(tsdial.Dialer)
24+
logf := func(format string, args ...interface{}) {
25+
if !t.Failed() {
26+
t.Logf(format, args...)
27+
}
28+
}
29+
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
30+
Tun: tunDev,
31+
Dialer: dialer,
32+
})
33+
if err != nil {
34+
t.Fatal(err)
35+
}
36+
defer eng.Close()
37+
ig, ok := eng.(wgengine.InternalsGetter)
38+
if !ok {
39+
t.Fatal("not an InternalsGetter")
40+
}
41+
tunWrap, magicSock, ok := ig.GetInternals()
42+
if !ok {
43+
t.Fatal("failed to get internals")
44+
}
45+
46+
ns, err := Create(logf, tunWrap, eng, magicSock, dialer)
47+
if err != nil {
48+
t.Fatal(err)
49+
}
50+
defer ns.Close()
51+
ns.ProcessLocalIPs = true
52+
if err := ns.Start(); err != nil {
53+
t.Fatalf("Start: %v", err)
54+
}
55+
ns.atomicIsLocalIPFunc.Store(func(netaddr.IP) bool { return true })
56+
57+
pkt := &packet.Parsed{}
58+
const N = 10_000
59+
ms0 := getMemStats()
60+
for i := 0; i < N; i++ {
61+
outcome := ns.injectInbound(pkt, tunWrap)
62+
if outcome != filter.DropSilently {
63+
t.Fatalf("got outcome %v; want DropSilently", outcome)
64+
}
65+
}
66+
ms1 := getMemStats()
67+
if grew := int64(ms1.HeapObjects) - int64(ms0.HeapObjects); grew >= N {
68+
t.Fatalf("grew by %v (which is too much and >= the %v packets we sent)", grew, N)
69+
}
70+
}
71+
72+
func getMemStats() (ms runtime.MemStats) {
73+
runtime.GC()
74+
runtime.ReadMemStats(&ms)
75+
return
76+
}

0 commit comments

Comments
 (0)