user_ent_hash_build_task() called getfilecon() on /proc/PID/fd/N for every socket, even without -z, and the path was resolved again after readlink() had seen a socket. The owner of the descriptor could swap in a file on a FUSE mount it serves and hang ss -p in getxattr. Only look up the context for -z, and read it through an O_PATH descriptor after checking with readlink(), which does not call into the file's filesystem, that it is still the same socket. Reported-by: Anthropic OSS scanner (ANT-2026-EPMKLNM2) Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Stephen Hemminger --- misc/ss.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index 74cb3be1..4b3d1fa4 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -533,6 +533,38 @@ static void user_ent_add(unsigned int ino, char *task, #define MAX_PATH_LEN 1024 +/* The owner of a descriptor can point $PROC_ROOT/$ID/fd/$FD elsewhere + * (e.g. at a file on a FUSE mount it serves) after readlink() saw a socket. + * Pin the object and check it is still that socket before reading its label. + * The check uses readlink(), which does not call into the filesystem of + * the pinned object. + */ +static int user_ent_sock_context(const char *path, unsigned int ino, + char **context) +{ + unsigned int pinned; + char self[64]; + char lnk[64]; + ssize_t len; + int ret = -1; + int fd; + + fd = open(path, O_PATH | O_CLOEXEC); + if (fd < 0) + return -1; + + snprintf(self, sizeof(self), "/proc/self/fd/%d", fd); + len = readlink(self, lnk, sizeof(lnk) - 1); + if (len > 0) { + lnk[len] = '\0'; + if (sscanf(lnk, "socket:[%u]", &pinned) == 1 && pinned == ino) + ret = getfilecon(self, context); + } + + close(fd); + return ret; +} + static void user_ent_hash_build_task(char *path, int pid, int tid) { const char *no_ctx = "unavailable"; @@ -580,7 +612,8 @@ static void user_ent_hash_build_task(char *path, int pid, int tid) if (sscanf(lnk, "socket:[%u]", &ino) != 1) continue; - if (getfilecon(path, &sock_context) <= 0) + if (!show_sock_ctx || + user_ent_sock_context(path, ino, &sock_context) <= 0) sock_context = strdup(no_ctx); if (task[0] == '\0') { -- 2.53.0