{"record":{"id":"3a95a0629f34ffa4","repo":"slackhq/nebula","slug":"failed-to-get-syscall-conn-for-tun-w","errorCode":null,"errorMessage":"failed to get syscall conn for tun: %w","messagePattern":"failed to get syscall conn for tun: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_netbsd.go","lineNumber":147,"sourceCode":"\t\t_ = unix.Close(t.fd)\n\n\t\ts, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer syscall.Close(s)\n\n\t\tifr := ifreq{Name: t.deviceBytes()}\n\t\terr = ioctl(uintptr(s), syscall.SIOCIFDESTROY, uintptr(unsafe.Pointer(&ifr)))\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (t *tun) Read(to []byte) (int, error) {\n\trc, err := t.f.SyscallConn()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to get syscall conn for tun: %w\", err)\n\t}\n\n\tvar errno syscall.Errno\n\tvar n uintptr\n\terr = rc.Read(func(fd uintptr) bool {\n\t\t// first 4 bytes is protocol family, in network byte order\n\t\thead := [4]byte{}\n\t\tiovecs := []syscall.Iovec{\n\t\t\t{&head[0], 4},\n\t\t\t{&to[0], uint64(len(to))},\n\t\t}\n\n\t\tn, _, errno = syscall.Syscall(syscall.SYS_READV, fd, uintptr(unsafe.Pointer(&iovecs[0])), uintptr(2))\n\t\tif errno.Temporary() {\n\t\t\t// We got an EAGAIN, EINTR, or EWOULDBLOCK, go again\n\t\t\treturn false\n\t\t}\n\t\treturn true","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_netbsd.go#L129-L165","documentation":"Read() calls f.SyscallConn() to obtain a raw syscall.RawConn for the TUN file before issuing the blocking read. If Go's runtime cannot produce a syscall conn for the file descriptor, the read cannot proceed and this error wraps the cause.","triggerScenarios":"t.f is nil or already closed when Read is invoked, or the *os.File wraps something that cannot expose a SyscallConn (unexpected file type).","commonSituations":"Read loop racing with Close() during shutdown so the file is already finalized; mis-constructed tun objects in tests.","solutions":["Stop the Read loop before calling Close() (signal via context/channel)","Handle os.ErrClosed from Read as a normal shutdown condition, not a fault","Verify the tun was created through newTun so t.f is a valid *os.File"],"exampleFix":"// before\ngo tun.Read(buf)\ntun.Close()\n// after\ndone := make(chan struct{})\ngo func() { defer close(done); tun.Read(buf) }()\ntun.Close()\n<-done","handlingStrategy":"try-catch","validationCode":"// before reading\nif tun == nil || tun.f == nil { return errors.New(\"tun not initialized\") }","typeGuard":null,"tryCatchPattern":"n, err := tun.Read(buf)\nif err != nil {\n    if errors.Is(err, os.ErrClosed) || strings.Contains(err.Error(), \"syscall conn\") {\n        return // shutdown path\n    }\n    return fmt.Errorf(\"tun read: %w\", err)\n}","preventionTips":["Stop all Read loops before Close()","Use a done channel/context to coordinate reader goroutines","Only construct tun via the library's newTun"],"tags":["netbsd","tun","read","file-descriptor"],"backgroundTag":"tun-syscall-conn-failed","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}