diff --git a/portlist/portlist_linux.go b/portlist/portlist_linux.go index abdc134ff..8b7555872 100644 --- a/portlist/portlist_linux.go +++ b/portlist/portlist_linux.go @@ -10,12 +10,15 @@ "io" "io/ioutil" "os" + "runtime" "sort" "strconv" "strings" + "syscall" "time" "golang.org/x/sys/unix" + "tailscale.com/syncs" ) // Reading the sockfiles on Linux is very fast, so we can do it often. @@ -26,13 +29,30 @@ var sockfiles = []string{"/proc/net/tcp", "/proc/net/udp"} var protos = []string{"tcp", "udp"} +var sawProcNetPermissionErr syncs.AtomicBool + func listPorts() (List, error) { + if sawProcNetPermissionErr.Get() { + return nil, nil + } l := []Port{} for pi, fname := range sockfiles { proto := protos[pi] + // Android 10+ doesn't allow access to this anymore. + // https://developer.android.com/about/versions/10/privacy/changes#proc-net-filesystem + // Ignore it rather than have the system log about our violation. + if runtime.GOOS == "android" && syscall.Access(fname, unix.R_OK) != nil { + sawProcNetPermissionErr.Set(true) + return nil, nil + } + f, err := os.Open(fname) + if os.IsPermission(err) { + sawProcNetPermissionErr.Set(true) + return nil, nil + } if err != nil { return nil, fmt.Errorf("%s: %s", fname, err) } @@ -96,7 +116,18 @@ func addProcesses(pl []Port) ([]Port, error) { } err := foreachPID(func(pid string) error { - fdDir, err := os.Open(fmt.Sprintf("/proc/%s/fd", pid)) + fdPath := fmt.Sprintf("/proc/%s/fd", pid) + + // Android logs a bunch of audit violations in logcat + // if we try to open things we don't have access + // to. So on Android only, ask if we have permission + // rather than just trying it to determine whether we + // have permission. + if runtime.GOOS == "android" && syscall.Access(fdPath, unix.R_OK) != nil { + return nil + } + + fdDir, err := os.Open(fdPath) if err != nil { // Can't open fd list for this pid. Maybe // don't have access. Ignore it.