tun: fix data race on name field

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2020-02-28 09:10:16 -08:00 committed by Jason A. Donenfeld
parent abd287159e
commit 85a45a9651

View file

@ -31,7 +31,6 @@ const (
type NativeTun struct { type NativeTun struct {
tunFile *os.File tunFile *os.File
index int32 // if index index int32 // if index
name string // name of interface
errors chan error // async error handling errors chan error // async error handling
events chan Event // device related events events chan Event // device related events
nopi bool // the device was passed IFF_NO_PI nopi bool // the device was passed IFF_NO_PI
@ -39,6 +38,10 @@ type NativeTun struct {
netlinkCancel *rwcancel.RWCancel netlinkCancel *rwcancel.RWCancel
hackListenerClosed sync.Mutex hackListenerClosed sync.Mutex
statusListenersShutdown chan struct{} statusListenersShutdown chan struct{}
nameOnce sync.Once // guards calling initNameCache, which sets following fields
nameCache string // name of interface
nameErr error
} }
func (tun *NativeTun) File() *os.File { func (tun *NativeTun) File() *os.File {
@ -192,6 +195,11 @@ func getIFIndex(name string) (int32, error) {
} }
func (tun *NativeTun) setMTU(n int) error { func (tun *NativeTun) setMTU(n int) error {
name, err := tun.Name()
if err != nil {
return err
}
// open datagram socket // open datagram socket
fd, err := unix.Socket( fd, err := unix.Socket(
unix.AF_INET, unix.AF_INET,
@ -206,9 +214,8 @@ func (tun *NativeTun) setMTU(n int) error {
defer unix.Close(fd) defer unix.Close(fd)
// do ioctl call // do ioctl call
var ifr [ifReqSize]byte var ifr [ifReqSize]byte
copy(ifr[:], tun.name) copy(ifr[:], name)
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
unix.SYS_IOCTL, unix.SYS_IOCTL,
@ -225,6 +232,11 @@ func (tun *NativeTun) setMTU(n int) error {
} }
func (tun *NativeTun) MTU() (int, error) { func (tun *NativeTun) MTU() (int, error) {
name, err := tun.Name()
if err != nil {
return 0, err
}
// open datagram socket // open datagram socket
fd, err := unix.Socket( fd, err := unix.Socket(
unix.AF_INET, unix.AF_INET,
@ -241,7 +253,7 @@ func (tun *NativeTun) MTU() (int, error) {
// do ioctl call // do ioctl call
var ifr [ifReqSize]byte var ifr [ifReqSize]byte
copy(ifr[:], tun.name) copy(ifr[:], name)
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
unix.SYS_IOCTL, unix.SYS_IOCTL,
uintptr(fd), uintptr(fd),
@ -256,6 +268,15 @@ func (tun *NativeTun) MTU() (int, error) {
} }
func (tun *NativeTun) Name() (string, error) { func (tun *NativeTun) Name() (string, error) {
tun.nameOnce.Do(tun.initNameCache)
return tun.nameCache, tun.nameErr
}
func (tun *NativeTun) initNameCache() {
tun.nameCache, tun.nameErr = tun.nameSlow()
}
func (tun *NativeTun) nameSlow() (string, error) {
sysconn, err := tun.tunFile.SyscallConn() sysconn, err := tun.tunFile.SyscallConn()
if err != nil { if err != nil {
return "", err return "", err
@ -276,13 +297,11 @@ func (tun *NativeTun) Name() (string, error) {
if errno != 0 { if errno != 0 {
return "", errors.New("failed to get name of TUN device: " + errno.Error()) return "", errors.New("failed to get name of TUN device: " + errno.Error())
} }
nullStr := ifr[:] name := ifr[:]
i := bytes.IndexByte(nullStr, 0) if i := bytes.IndexByte(name, 0); i != -1 {
if i != -1 { name = name[:i]
nullStr = nullStr[:i]
} }
tun.name = string(nullStr) return string(name), nil
return tun.name, nil
} }
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
@ -402,16 +421,15 @@ func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
statusListenersShutdown: make(chan struct{}), statusListenersShutdown: make(chan struct{}),
nopi: false, nopi: false,
} }
var err error
_, err = tun.Name() name, err := tun.Name()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// start event listener // start event listener
tun.index, err = getIFIndex(tun.name) tun.index, err = getIFIndex(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }