Fix Linux process path lookup after privilege drop

This commit is contained in:
世界
2026-09-06 15:51:35 +08:00
parent 4ef3432c0d
commit 0265f31f84
+24 -15
View File
@@ -22,7 +22,11 @@ import (
"github.com/sagernet/sing/contrab/maphash"
)
const pathProc = "/proc"
const (
pathProc = "/proc"
processPathsAllUsers = ^uint32(0)
)
var _ Searcher = (*linuxSearcher)(nil)
@@ -123,25 +127,30 @@ func (s *linuxSearcher) resolveSocketByNetlink(network string, source netip.Addr
return dumpSocketDiag(family, protocol, source, destination)
}
// The socket keeps the uid it was created with, while /proc reflects the
// current uid of the process, so a socket created before a privilege drop
// only appears under a scan of all users.
func (s *linuxSearcher) findProcessPath(targetInode, uid uint32) (string, error) {
if cached, ok := s.processPathCache.Get(uid); ok {
if processPath, found := cached.entries[targetInode]; found {
for _, scanUID := range []uint32{uid, processPathsAllUsers} {
if cached, ok := s.processPathCache.Get(scanUID); ok {
if processPath, found := cached.entries[targetInode]; found {
return processPath, nil
}
}
processPaths, err := buildProcessPaths(scanUID)
if err != nil {
return "", err
}
s.processPathCache.Add(scanUID, &uidProcessPaths{entries: processPaths})
processPath, found := processPaths[targetInode]
if found {
return processPath, nil
}
}
processPaths, err := buildProcessPathsByUID(uid)
if err != nil {
return "", err
}
s.processPathCache.Add(uid, &uidProcessPaths{entries: processPaths})
processPath, found := processPaths[targetInode]
if !found {
return "", E.New("process of uid(", uid, "), inode(", targetInode, ") not found")
}
return processPath, nil
return "", E.New("process of uid(", uid, "), inode(", targetInode, ") not found")
}
func buildProcessPathsByUID(uid uint32) (map[uint32]string, error) {
func buildProcessPaths(uid uint32) (map[uint32]string, error) {
files, err := os.ReadDir(pathProc)
if err != nil {
return nil, err
@@ -159,7 +168,7 @@ func buildProcessPathsByUID(uid uint32) (map[uint32]string, error) {
}
return nil, err
}
if info.Sys().(*syscall.Stat_t).Uid != uid {
if uid != processPathsAllUsers && info.Sys().(*syscall.Stat_t).Uid != uid {
continue
}
processPath := filepath.Join(pathProc, file.Name())