{"record":{"id":"1b64ab61ef18411c","repo":"v2rayA/v2rayA","slug":"getprocessinfo-w","errorCode":null,"errorMessage":"getProcessInfo: %w","messagePattern":"getProcessInfo: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"service/common/netTools/netstat/netstat_unix.go","lineNumber":214,"sourceCode":"\nfunc getProcName(s string) string {\n\ti := strings.Index(s, \"(\")\n\tif i < 0 {\n\t\treturn \"\"\n\t}\n\ts = s[i+1:]\n\tj := strings.LastIndex(s, \")\")\n\tif j < 0 {\n\t\treturn \"\"\n\t}\n\treturn s[:j]\n}\n\nfunc getProcessInfo(pid string) (pn string, ppid string) {\n\tp := filepath.Join(pathProc, pid, \"stat\")\n\tb, err := os.ReadFile(p)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"getProcessInfo: %w\", err)\n\t\treturn\n\t}\n\tsp := bytes.Fields(b)\n\tpn = string(sp[1])\n\treturn getProcName(pn), string(sp[3])\n}\n\nfunc isProcessSocket(pid string, socketInode map[string]struct{}) string {\n\t// link name is of the form socket:[5860846]\n\tp := filepath.Join(pathProc, pid, \"fd\")\n\tf, err := os.Open(p)\n\tfns, err := f.Readdirnames(-1)\n\tf.Close()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tfor _, fn := range fns {\n\t\tlk, err := os.Readlink(filepath.Join(p, fn))","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/v2rayA/v2rayA/blob/71e5442fc548c05680ee55eae943e6c0afe9ac51/service/common/netTools/netstat/netstat_unix.go#L196-L232","documentation":"getProcessInfo reads /proc/<pid>/stat to extract the process name and PPID; if the read fails it wraps the OS error as 'getProcessInfo: %w' and returns zero values. Because the function returns named values, callers may silently receive empty pn/ppid — the error is created but NOT returned to callers, so it mostly shows up as lost error information plus empty process names.","triggerScenarios":"A PID directory listed by findProcessID/FillProcesses/Process disappears (process exits) between ReadDir and the stat read; permission denied on /proc/<pid>/stat for other users' processes (hidepid); kernel threads or zombie entries with unreadable stat.","commonSituations":"Race-heavy scans on busy systems where PIDs churn; unprivileged user scanning root-owned processes under hidepid=2; PID namespaces mismatch in containers; short-lived processes exiting during the scan.","solutions":["Accept empty results for vanished PIDs — rescan; transient races are the most common cause.","Run with privileges or relax hidepid so /proc/<pid>/stat of other users is readable.","Note the library bug: getProcessInfo assigns err but never returns it, so callers can't distinguish 'no such process' from 'not matched'; patch it to return an error if you need fidelity.","Verify with 'cat /proc/<pid>/stat' as the service user to confirm accessibility."],"exampleFix":"// before (library code): error swallowed\nb, err := os.ReadFile(p)\nif err != nil {\n    err = fmt.Errorf(\"getProcessInfo: %w\", err)\n    return // err assigned but never propagated\n}\n// after: propagate properly\nfunc getProcessInfo(pid string) (pn, ppid string, err error) {\n    b, err := os.ReadFile(filepath.Join(pathProc, pid, \"stat\"))\n    if err != nil { return \"\", \"\", fmt.Errorf(\"getProcessInfo: %w\", err) }\n    ...\n}","handlingStrategy":"retry","validationCode":"func statReadable(pid string) bool {\n    f, err := os.Open(filepath.Join(\"/proc\", pid, \"stat\"))\n    if err != nil { return false }\n    f.Close()\n    return true\n}","typeGuard":"func processInfoValid(pn, ppid string) bool { return pn != \"\" && ppid != \"\" }","tryCatchPattern":"pn, ppid := getProcessInfo(pid) // note: error is swallowed by the library\nif pn == \"\" {\n    time.Sleep(50 * time.Millisecond)\n    pn, ppid = getProcessInfo(pid) // retry once: PID may have raced away\n}\n","preventionTips":["Retry scans — /proc/<pid>/stat reads race with process exit on busy systems.","Run privileged or relax hidepid so other users' stat files are readable.","Know the library flaw: getProcessInfo never returns its error, so empty name means 'unreadable or gone' — verify with /proc/<pid>/comm when it matters.","Ignore kernel-thread PIDs (empty comm) instead of treating them as failures."],"tags":["linux","procfs","race-condition","error-swallowing"],"backgroundTag":"proc-entry-vanished","analyzedSha":"71e5442fc548c05680ee55eae943e6c0afe9ac51","analyzedAt":"2026-09-05T20:04:37.459Z","contentChangedAt":"2026-09-05T20:04:37.459Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}