Skip to content

Commit da879a2

Browse files
smirainsomniacslk
authored andcommitted
fix: add CLOEXEC flag to socket creates directly
Go runtime adds `CLOEXEC` flags automatically, but when using raw syscalls, we have to do it manually. Without this flag, file descriptors leak to the child processes. Signed-off-by: Andrey Smirnov <[email protected]>
1 parent f80a195 commit da879a2

File tree

6 files changed

+61
-4
lines changed

6 files changed

+61
-4
lines changed

dhcpv4/client4/client.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/insomniacslk/dhcp/dhcpv4"
13+
"github.com/insomniacslk/dhcp/internal/xsocket"
1314
"golang.org/x/net/ipv4"
1415
"golang.org/x/sys/unix"
1516
)
@@ -77,7 +78,7 @@ func MakeRawUDPPacket(payload []byte, serverAddr, clientAddr net.UDPAddr) ([]byt
7778

7879
// makeRawSocket creates a socket that can be passed to unix.Sendto.
7980
func makeRawSocket(ifname string) (int, error) {
80-
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_RAW)
81+
fd, err := xsocket.CloexecSocket(unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_RAW)
8182
if err != nil {
8283
return fd, err
8384
}
@@ -123,7 +124,7 @@ func htons(v uint16) uint16 {
123124
}
124125

125126
func makeListeningSocketWithCustomPort(ifname string, port int) (int, error) {
126-
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, int(htons(unix.ETH_P_IP)))
127+
fd, err := xsocket.CloexecSocket(unix.AF_PACKET, unix.SOCK_DGRAM, int(htons(unix.ETH_P_IP)))
127128
if err != nil {
128129
return fd, err
129130
}

dhcpv4/server4/conn_unix.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build !windows
12
// +build !windows
23

34
package server4
@@ -9,6 +10,7 @@ import (
910
"os"
1011

1112
"github.com/insomniacslk/dhcp/dhcpv4"
13+
"github.com/insomniacslk/dhcp/internal/xsocket"
1214
"golang.org/x/sys/unix"
1315
)
1416

@@ -17,7 +19,7 @@ import (
1719
//
1820
// The interface must already be configured.
1921
func NewIPv4UDPConn(iface string, addr *net.UDPAddr) (*net.UDPConn, error) {
20-
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
22+
fd, err := xsocket.CloexecSocket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
2123
if err != nil {
2224
return nil, fmt.Errorf("cannot get a UDP socket: %v", err)
2325
}

dhcpv6/server6/conn_unix.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build !windows
12
// +build !windows
23

34
package server6
@@ -9,6 +10,7 @@ import (
910
"os"
1011

1112
"github.com/insomniacslk/dhcp/interfaces"
13+
"github.com/insomniacslk/dhcp/internal/xsocket"
1214
"golang.org/x/sys/unix"
1315
)
1416

@@ -18,7 +20,7 @@ import (
1820
//
1921
// The interface must already be configured.
2022
func NewIPv6UDPConn(iface string, addr *net.UDPAddr) (*net.UDPConn, error) {
21-
fd, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
23+
fd, err := xsocket.CloexecSocket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
2224
if err != nil {
2325
return nil, fmt.Errorf("cannot get a UDP socket: %v", err)
2426
}

internal/xsocket/xsocket.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package xsocket
2+
3+
import (
4+
"syscall"
5+
6+
"golang.org/x/sys/unix"
7+
)
8+
9+
// CloexecSocket creates a new socket with the close-on-exec flag set.
10+
//
11+
// If the OS doesn't support the close-on-exec flag, this function will try a workaround.
12+
func CloexecSocket(domain, typ, proto int) (int, error) {
13+
fd, err := socketCloexec(domain, typ, proto)
14+
if err == nil {
15+
return fd, nil
16+
}
17+
18+
if err == unix.EINVAL || err == unix.EPROTONOSUPPORT {
19+
// SOCK_CLOEXEC is not supported, try without it, but avoid racing with fork/exec
20+
syscall.ForkLock.RLock()
21+
defer syscall.ForkLock.RUnlock()
22+
23+
fd, err = unix.Socket(domain, typ, proto)
24+
if err != nil {
25+
return -1, err
26+
}
27+
28+
unix.CloseOnExec(fd)
29+
30+
return fd, nil
31+
}
32+
33+
return fd, err
34+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build dragonfly || freebsd || linux || netbsd || openbsd
2+
3+
package xsocket
4+
5+
import "golang.org/x/sys/unix"
6+
7+
func socketCloexec(domain, typ, proto int) (int, error) {
8+
return unix.Socket(domain, typ|unix.SOCK_CLOEXEC, proto)
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !(dragonfly || freebsd || linux || netbsd || openbsd)
2+
3+
package xsocket
4+
5+
import "golang.org/x/sys/unix"
6+
7+
func socketCloexec(domain, typ, proto int) (int, error) {
8+
return unix.Socket(domain, typ, proto)
9+
}

0 commit comments

Comments
 (0)