Skip to content

Commit c3db64c

Browse files
achille-rousselgopherbot
authored andcommitted
net: fix panic when calling net.Listen or net.Dial on wasip1
Address a panic that was caused by net.Dial/net.Listen entering the fake network stack and assuming that the addresses would be of type *TCPAddr, where in fact they could have been *UDPAddr or *UnixAddr as well. The fix consist in implementing the fake network facility for udp and unix addresses, preventing the assumed type assertion to TCPAddr from triggering a panic. New tests are added to verify that using the fake network from the exported functions of the net package satisfies the minimal requirement of being able to create a listener and establish a connection for all the supported network types. Fixes #60012 Fixes #60739 Change-Id: I2688f1a0a7c6c9894ad3d137a5d311192c77a9b2 Reviewed-on: https://go-review.googlesource.com/c/go/+/502315 Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Dmitri Shuralyov <[email protected]> Reviewed-by: Johan Brandhorst-Satzkorn <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Dmitri Shuralyov <[email protected]>
1 parent 711ef8b commit c3db64c

File tree

2 files changed

+312
-25
lines changed

2 files changed

+312
-25
lines changed

src/net/net_fake.go

Lines changed: 109 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
)
2121

2222
var listenersMu sync.Mutex
23-
var listeners = make(map[string]*netFD)
23+
var listeners = make(map[fakeNetAddr]*netFD)
2424

2525
var portCounterMu sync.Mutex
2626
var portCounter = 0
@@ -32,13 +32,16 @@ func nextPort() int {
3232
return portCounter
3333
}
3434

35+
type fakeNetAddr struct {
36+
network string
37+
address string
38+
}
39+
3540
type fakeNetFD struct {
36-
listener bool
37-
laddr Addr
41+
listener fakeNetAddr
3842
r *bufferedPipe
3943
w *bufferedPipe
4044
incoming chan *netFD
41-
4245
closedMu sync.Mutex
4346
closed bool
4447
}
@@ -51,32 +54,110 @@ func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only
5154
return fakelistener(fd, laddr)
5255
}
5356
fd2 := &netFD{family: family, sotype: sotype, net: net}
54-
return fakeconn(fd, fd2, raddr)
57+
return fakeconn(fd, fd2, laddr, raddr)
58+
}
59+
60+
func fakeIPAndPort(ip IP, port int) (IP, int) {
61+
if ip == nil {
62+
ip = IPv4(127, 0, 0, 1)
63+
}
64+
if port == 0 {
65+
port = nextPort()
66+
}
67+
return ip, port
68+
}
69+
70+
func fakeTCPAddr(addr *TCPAddr) *TCPAddr {
71+
var ip IP
72+
var port int
73+
var zone string
74+
if addr != nil {
75+
ip, port, zone = addr.IP, addr.Port, addr.Zone
76+
}
77+
ip, port = fakeIPAndPort(ip, port)
78+
return &TCPAddr{IP: ip, Port: port, Zone: zone}
79+
}
80+
81+
func fakeUDPAddr(addr *UDPAddr) *UDPAddr {
82+
var ip IP
83+
var port int
84+
var zone string
85+
if addr != nil {
86+
ip, port, zone = addr.IP, addr.Port, addr.Zone
87+
}
88+
ip, port = fakeIPAndPort(ip, port)
89+
return &UDPAddr{IP: ip, Port: port, Zone: zone}
90+
}
91+
92+
func fakeUnixAddr(sotype int, addr *UnixAddr) *UnixAddr {
93+
var net, name string
94+
if addr != nil {
95+
name = addr.Name
96+
}
97+
switch sotype {
98+
case syscall.SOCK_DGRAM:
99+
net = "unixgram"
100+
case syscall.SOCK_SEQPACKET:
101+
net = "unixpacket"
102+
default:
103+
net = "unix"
104+
}
105+
return &UnixAddr{Net: net, Name: name}
55106
}
56107

57108
func fakelistener(fd *netFD, laddr sockaddr) (*netFD, error) {
58-
l := laddr.(*TCPAddr)
59-
fd.laddr = &TCPAddr{
60-
IP: l.IP,
61-
Port: nextPort(),
62-
Zone: l.Zone,
109+
switch l := laddr.(type) {
110+
case *TCPAddr:
111+
laddr = fakeTCPAddr(l)
112+
case *UDPAddr:
113+
laddr = fakeUDPAddr(l)
114+
case *UnixAddr:
115+
if l.Name == "" {
116+
return nil, syscall.ENOENT
117+
}
118+
laddr = fakeUnixAddr(fd.sotype, l)
119+
default:
120+
return nil, syscall.EOPNOTSUPP
63121
}
122+
123+
listener := fakeNetAddr{
124+
network: laddr.Network(),
125+
address: laddr.String(),
126+
}
127+
64128
fd.fakeNetFD = &fakeNetFD{
65-
listener: true,
66-
laddr: fd.laddr,
129+
listener: listener,
67130
incoming: make(chan *netFD, 1024),
68131
}
132+
133+
fd.laddr = laddr
69134
listenersMu.Lock()
70-
listeners[fd.laddr.(*TCPAddr).String()] = fd
71-
listenersMu.Unlock()
135+
defer listenersMu.Unlock()
136+
if _, exists := listeners[listener]; exists {
137+
return nil, syscall.EADDRINUSE
138+
}
139+
listeners[listener] = fd
72140
return fd, nil
73141
}
74142

75-
func fakeconn(fd *netFD, fd2 *netFD, raddr sockaddr) (*netFD, error) {
76-
fd.laddr = &TCPAddr{
77-
IP: IPv4(127, 0, 0, 1),
78-
Port: nextPort(),
143+
func fakeconn(fd *netFD, fd2 *netFD, laddr, raddr sockaddr) (*netFD, error) {
144+
switch r := raddr.(type) {
145+
case *TCPAddr:
146+
r = fakeTCPAddr(r)
147+
raddr = r
148+
laddr = fakeTCPAddr(laddr.(*TCPAddr))
149+
case *UDPAddr:
150+
r = fakeUDPAddr(r)
151+
raddr = r
152+
laddr = fakeUDPAddr(laddr.(*UDPAddr))
153+
case *UnixAddr:
154+
r = fakeUnixAddr(fd.sotype, r)
155+
raddr = r
156+
laddr = &UnixAddr{Net: r.Net, Name: r.Name}
157+
default:
158+
return nil, syscall.EAFNOSUPPORT
79159
}
160+
fd.laddr = laddr
80161
fd.raddr = raddr
81162

82163
fd.fakeNetFD = &fakeNetFD{
@@ -90,15 +171,18 @@ func fakeconn(fd *netFD, fd2 *netFD, raddr sockaddr) (*netFD, error) {
90171

91172
fd2.laddr = fd.raddr
92173
fd2.raddr = fd.laddr
174+
175+
listener := fakeNetAddr{
176+
network: fd.raddr.Network(),
177+
address: fd.raddr.String(),
178+
}
93179
listenersMu.Lock()
94-
l, ok := listeners[fd.raddr.(*TCPAddr).String()]
180+
defer listenersMu.Unlock()
181+
l, ok := listeners[listener]
95182
if !ok {
96-
listenersMu.Unlock()
97183
return nil, syscall.ECONNREFUSED
98184
}
99185
l.incoming <- fd2
100-
listenersMu.Unlock()
101-
102186
return fd, nil
103187
}
104188

@@ -119,11 +203,11 @@ func (fd *fakeNetFD) Close() error {
119203
fd.closed = true
120204
fd.closedMu.Unlock()
121205

122-
if fd.listener {
206+
if fd.listener != (fakeNetAddr{}) {
123207
listenersMu.Lock()
124-
delete(listeners, fd.laddr.String())
208+
delete(listeners, fd.listener)
125209
close(fd.incoming)
126-
fd.listener = false
210+
fd.listener = fakeNetAddr{}
127211
listenersMu.Unlock()
128212
return nil
129213
}

src/net/net_fake_test.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Copyright 2023 The Go 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+
//go:build js || wasip1
6+
7+
package net
8+
9+
// GOOS=js and GOOS=wasip1 do not have typical socket networking capabilities
10+
// found on other platforms. To help run test suites of the stdlib packages,
11+
// an in-memory "fake network" facility is implemented.
12+
//
13+
// The tests in this files are intended to validate the behavior of the fake
14+
// network stack on these platforms.
15+
16+
import "testing"
17+
18+
func TestFakeConn(t *testing.T) {
19+
tests := []struct {
20+
name string
21+
listen func() (Listener, error)
22+
dial func(Addr) (Conn, error)
23+
addr func(*testing.T, Addr)
24+
}{
25+
{
26+
name: "Listener:tcp",
27+
listen: func() (Listener, error) {
28+
return Listen("tcp", ":0")
29+
},
30+
dial: func(addr Addr) (Conn, error) {
31+
return Dial(addr.Network(), addr.String())
32+
},
33+
addr: testFakeTCPAddr,
34+
},
35+
36+
{
37+
name: "ListenTCP:tcp",
38+
listen: func() (Listener, error) {
39+
// Creating a listening TCP connection with a nil address must
40+
// select an IP address on localhost with a random port.
41+
// This test verifies that the fake network facility does that.
42+
return ListenTCP("tcp", nil)
43+
},
44+
dial: func(addr Addr) (Conn, error) {
45+
// Connecting a listening TCP connection will select a local
46+
// address on the local network and connects to the destination
47+
// address.
48+
return DialTCP("tcp", nil, addr.(*TCPAddr))
49+
},
50+
addr: testFakeTCPAddr,
51+
},
52+
53+
{
54+
name: "ListenUnix:unix",
55+
listen: func() (Listener, error) {
56+
return ListenUnix("unix", &UnixAddr{Name: "test"})
57+
},
58+
dial: func(addr Addr) (Conn, error) {
59+
return DialUnix("unix", nil, addr.(*UnixAddr))
60+
},
61+
addr: testFakeUnixAddr("unix", "test"),
62+
},
63+
64+
{
65+
name: "ListenUnix:unixpacket",
66+
listen: func() (Listener, error) {
67+
return ListenUnix("unixpacket", &UnixAddr{Name: "test"})
68+
},
69+
dial: func(addr Addr) (Conn, error) {
70+
return DialUnix("unixpacket", nil, addr.(*UnixAddr))
71+
},
72+
addr: testFakeUnixAddr("unixpacket", "test"),
73+
},
74+
}
75+
76+
for _, test := range tests {
77+
t.Run(test.name, func(t *testing.T) {
78+
l, err := test.listen()
79+
if err != nil {
80+
t.Fatal(err)
81+
}
82+
defer l.Close()
83+
test.addr(t, l.Addr())
84+
85+
c, err := test.dial(l.Addr())
86+
if err != nil {
87+
t.Fatal(err)
88+
}
89+
defer c.Close()
90+
test.addr(t, c.LocalAddr())
91+
test.addr(t, c.RemoteAddr())
92+
})
93+
}
94+
}
95+
96+
func TestFakePacketConn(t *testing.T) {
97+
tests := []struct {
98+
name string
99+
listen func() (PacketConn, error)
100+
dial func(Addr) (Conn, error)
101+
addr func(*testing.T, Addr)
102+
}{
103+
{
104+
name: "ListenPacket:udp",
105+
listen: func() (PacketConn, error) {
106+
return ListenPacket("udp", ":0")
107+
},
108+
dial: func(addr Addr) (Conn, error) {
109+
return Dial(addr.Network(), addr.String())
110+
},
111+
addr: testFakeUDPAddr,
112+
},
113+
114+
{
115+
name: "ListenUDP:udp",
116+
listen: func() (PacketConn, error) {
117+
// Creating a listening UDP connection with a nil address must
118+
// select an IP address on localhost with a random port.
119+
// This test verifies that the fake network facility does that.
120+
return ListenUDP("udp", nil)
121+
},
122+
dial: func(addr Addr) (Conn, error) {
123+
// Connecting a listening UDP connection will select a local
124+
// address on the local network and connects to the destination
125+
// address.
126+
return DialUDP("udp", nil, addr.(*UDPAddr))
127+
},
128+
addr: testFakeUDPAddr,
129+
},
130+
131+
{
132+
name: "ListenUnixgram:unixgram",
133+
listen: func() (PacketConn, error) {
134+
return ListenUnixgram("unixgram", &UnixAddr{Name: "test"})
135+
},
136+
dial: func(addr Addr) (Conn, error) {
137+
return DialUnix("unixgram", nil, addr.(*UnixAddr))
138+
},
139+
addr: testFakeUnixAddr("unixgram", "test"),
140+
},
141+
}
142+
143+
for _, test := range tests {
144+
t.Run(test.name, func(t *testing.T) {
145+
l, err := test.listen()
146+
if err != nil {
147+
t.Fatal(err)
148+
}
149+
defer l.Close()
150+
test.addr(t, l.LocalAddr())
151+
152+
c, err := test.dial(l.LocalAddr())
153+
if err != nil {
154+
t.Fatal(err)
155+
}
156+
defer c.Close()
157+
test.addr(t, c.LocalAddr())
158+
test.addr(t, c.RemoteAddr())
159+
})
160+
}
161+
}
162+
163+
func testFakeTCPAddr(t *testing.T, addr Addr) {
164+
t.Helper()
165+
if a, ok := addr.(*TCPAddr); !ok {
166+
t.Errorf("Addr is not *TCPAddr: %T", addr)
167+
} else {
168+
testFakeNetAddr(t, a.IP, a.Port)
169+
}
170+
}
171+
172+
func testFakeUDPAddr(t *testing.T, addr Addr) {
173+
t.Helper()
174+
if a, ok := addr.(*UDPAddr); !ok {
175+
t.Errorf("Addr is not *UDPAddr: %T", addr)
176+
} else {
177+
testFakeNetAddr(t, a.IP, a.Port)
178+
}
179+
}
180+
181+
func testFakeNetAddr(t *testing.T, ip IP, port int) {
182+
t.Helper()
183+
if port == 0 {
184+
t.Error("network address is missing port")
185+
} else if len(ip) == 0 {
186+
t.Error("network address is missing IP")
187+
} else if !ip.Equal(IPv4(127, 0, 0, 1)) {
188+
t.Errorf("network address has wrong IP: %s", ip)
189+
}
190+
}
191+
192+
func testFakeUnixAddr(net, name string) func(*testing.T, Addr) {
193+
return func(t *testing.T, addr Addr) {
194+
t.Helper()
195+
if a, ok := addr.(*UnixAddr); !ok {
196+
t.Errorf("Addr is not *UnixAddr: %T", addr)
197+
} else if a.Net != net {
198+
t.Errorf("unix address has wrong net: want=%q got=%q", net, a.Net)
199+
} else if a.Name != name {
200+
t.Errorf("unix address has wrong name: want=%q got=%q", name, a.Name)
201+
}
202+
}
203+
}

0 commit comments

Comments
 (0)