]> git.ipfire.org Git - thirdparty/wireguard-go.git/commitdiff
tun: fix data race on name field
authorBrad Fitzpatrick <bradfitz@tailscale.com>
Fri, 28 Feb 2020 17:10:16 +0000 (09:10 -0800)
committerJason A. Donenfeld <Jason@zx2c4.com>
Sat, 2 May 2020 07:46:42 +0000 (01:46 -0600)
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
tun/tun_linux.go

index 17b8822f92724639c2f7d97294319420fb647d4d..2f97ebbc536712980019576f4a597a725c2da202 100644 (file)
@@ -31,7 +31,6 @@ const (
 type NativeTun struct {
        tunFile                 *os.File
        index                   int32      // if index
-       name                    string     // name of interface
        errors                  chan error // async error handling
        events                  chan Event // device related events
        nopi                    bool       // the device was passed IFF_NO_PI
@@ -39,6 +38,10 @@ type NativeTun struct {
        netlinkCancel           *rwcancel.RWCancel
        hackListenerClosed      sync.Mutex
        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 {
@@ -192,6 +195,11 @@ func getIFIndex(name string) (int32, error) {
 }
 
 func (tun *NativeTun) setMTU(n int) error {
+       name, err := tun.Name()
+       if err != nil {
+               return err
+       }
+
        // open datagram socket
        fd, err := unix.Socket(
                unix.AF_INET,
@@ -206,9 +214,8 @@ func (tun *NativeTun) setMTU(n int) error {
        defer unix.Close(fd)
 
        // do ioctl call
-
        var ifr [ifReqSize]byte
-       copy(ifr[:], tun.name)
+       copy(ifr[:], name)
        *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
        _, _, errno := unix.Syscall(
                unix.SYS_IOCTL,
@@ -225,6 +232,11 @@ func (tun *NativeTun) setMTU(n int) error {
 }
 
 func (tun *NativeTun) MTU() (int, error) {
+       name, err := tun.Name()
+       if err != nil {
+               return 0, err
+       }
+
        // open datagram socket
        fd, err := unix.Socket(
                unix.AF_INET,
@@ -241,7 +253,7 @@ func (tun *NativeTun) MTU() (int, error) {
        // do ioctl call
 
        var ifr [ifReqSize]byte
-       copy(ifr[:], tun.name)
+       copy(ifr[:], name)
        _, _, errno := unix.Syscall(
                unix.SYS_IOCTL,
                uintptr(fd),
@@ -256,6 +268,15 @@ func (tun *NativeTun) MTU() (int, 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()
        if err != nil {
                return "", err
@@ -276,13 +297,11 @@ func (tun *NativeTun) Name() (string, error) {
        if errno != 0 {
                return "", errors.New("failed to get name of TUN device: " + errno.Error())
        }
-       nullStr := ifr[:]
-       i := bytes.IndexByte(nullStr, 0)
-       if i != -1 {
-               nullStr = nullStr[:i]
+       name := ifr[:]
+       if i := bytes.IndexByte(name, 0); i != -1 {
+               name = name[:i]
        }
-       tun.name = string(nullStr)
-       return tun.name, nil
+       return string(name), nil
 }
 
 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{}),
                nopi:                    false,
        }
-       var err error
 
-       _, err = tun.Name()
+       name, err := tun.Name()
        if err != nil {
                return nil, err
        }
 
        // start event listener
 
-       tun.index, err = getIFIndex(tun.name)
+       tun.index, err = getIFIndex(name)
        if err != nil {
                return nil, err
        }