All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] linux-user: Filter /proc/*/task/ and validate tkill targets
@ 2026-04-14 23:58 Ali Raza
  2026-04-14 23:58 ` [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads Ali Raza
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Ali Raza @ 2026-04-14 23:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ali Raza, morgan

This series fixes a problem where QEMU user-mode exposes internal
host threads (RCU, TCG workers) to guest processes via /proc/*/task/
and allows the guest to signal them via tkill/tgkill.
 
Patch 1: Filters getdents/getdents64 on /proc/<pid>/task/
Patch 2: Validates tkill/tgkill targets against guest CPU list
Patch 3: Adds a multiarch test exercising both fixes

Signed-off-by: Ali Raza <elirazamumtaz@gmail.com>
---
Ali Raza (3):
      linux-user: Filter /proc/*/task/ to hide QEMU-internal threads
      linux-user: Validate tkill/tgkill targets are guest threads
      tests/tcg: Add test for /proc/self/task/ filtering and tkill validation

 linux-user/syscall.c                        | 114 +++++++++++++++++-
 tests/tcg/multiarch/Makefile.target         |   2 +
 tests/tcg/multiarch/linux/linux-proc-task.c | 178 ++++++++++++++++++++++++++++
 3 files changed, 292 insertions(+), 2 deletions(-)
---
base-commit: da6c4fe60fee30dd77267764d55b38af9cb89d4b
change-id: 20260415-master-3a0fa2ebf2d5

Best regards,
--  
Ali Raza <elirazamumtaz@gmail.com>



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads
  2026-04-14 23:58 [PATCH 0/3] linux-user: Filter /proc/*/task/ and validate tkill targets Ali Raza
@ 2026-04-14 23:58 ` Ali Raza
  2026-04-23 15:59   ` Helge Deller
  2026-04-14 23:58 ` [PATCH 2/3] linux-user: Validate tkill/tgkill targets are guest threads Ali Raza
  2026-04-14 23:58 ` [PATCH 3/3] tests/tcg: Add test for /proc/self/task/ filtering and tkill validation Ali Raza
  2 siblings, 1 reply; 6+ messages in thread
From: Ali Raza @ 2026-04-14 23:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ali Raza, morgan

When a guest process reads /proc/<pid>/task/ via getdents/getdents64,
the host kernel returns directory entries for all host threads in the
process, including QEMU-internal threads (RCU, TCG workers) that have
no guest CPUState.  This causes problems for guest libraries like
libcap's PSX that enumerate threads via /proc/self/task/ and send
signals to each one -- signals sent to QEMU-internal threads are never
handled, leading to deadlocks.

Add filtering to do_getdents() and do_getdents64() that detects when
the fd refers to a /proc/<pid>/task/ directory and skips entries whose
TID does not belong to a guest thread.  Guest thread TIDs are
identified by iterating the CPU list via CPU_FOREACH(), matching the
existing pattern used for /proc/self/stat num_threads.

This fixes the psx_test hang reported in containers using QEMU
user-mode emulation for cross-architecture builds.

Resolves: https://github.com/AndrewGMorgan/libcap_mirror/issues/6
Signed-off-by: Ali Raza (@locus-x64)
---
 linux-user/syscall.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index f4b74ad350..b5a912dc22 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -9108,6 +9108,50 @@ static int host_to_target_cpu_mask(const unsigned long *host_mask,
     return 0;
 }
 
+/*
+ * Check if a directory fd refers to /proc/<pid>/task/ for the current
+ * process.  Used to filter out QEMU-internal host threads (RCU, TCG)
+ * that are not guest threads from directory listings.
+ */
+static bool is_proc_pid_task_dir(int dirfd)
+{
+    char link_path[64];
+    char link_target[PATH_MAX];
+    ssize_t len;
+    char expected[80];
+    int expected_len;
+
+    snprintf(link_path, sizeof(link_path), "/proc/self/fd/%d", dirfd);
+    len = readlink(link_path, link_target, sizeof(link_target) - 1);
+    if (len < 0) {
+        return false;
+    }
+    link_target[len] = '\0';
+
+    expected_len = snprintf(expected, sizeof(expected),
+                            "/proc/%d/task", getpid());
+    return strncmp(link_target, expected, expected_len) == 0
+           && (link_target[expected_len] == '\0'
+               || link_target[expected_len] == '/');
+}
+
+/*
+ * Check if a given host TID belongs to a guest thread by looking it up
+ * in the CPU list.  Must be called under RCU read lock.
+ */
+static bool is_guest_tid(pid_t tid)
+{
+    CPUState *cpu;
+
+    CPU_FOREACH(cpu) {
+        TaskState *ts = get_task_state(cpu);
+        if (ts->ts_tid == tid) {
+            return true;
+        }
+    }
+    return false;
+}
+
 #ifdef TARGET_NR_getdents
 static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)
 {
@@ -9116,6 +9160,7 @@ static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)
     int hlen, hoff, toff;
     int hreclen, treclen;
     off_t prev_diroff = 0;
+    bool filter_task = is_proc_pid_task_dir(dirfd);
 
     hdirp = g_try_malloc(count);
     if (!hdirp) {
@@ -9150,6 +9195,23 @@ static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)
 
         namelen = strlen(hde->d_name);
         hreclen = hde->d_reclen;
+
+        /*
+         * Filter /proc/<pid>/task/ listings to hide QEMU-internal
+         * host threads (RCU, TCG) that have no guest CPUState.
+         */
+        if (filter_task) {
+            char *endp;
+            long tid = strtol(hde->d_name, &endp, 10);
+            if (tid > 0 && *endp == '\0') {
+                WITH_RCU_READ_LOCK_GUARD() {
+                    if (!is_guest_tid(tid)) {
+                        treclen = 0;
+                        continue;
+                    }
+                }
+            }
+        }
         treclen = offsetof(struct target_dirent, d_name) + namelen + 2;
         treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent));
 
@@ -9203,6 +9265,7 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)
     int hlen, hoff, toff;
     int hreclen, treclen;
     off_t prev_diroff = 0;
+    bool filter_task = is_proc_pid_task_dir(dirfd);
 
     hdirp = g_try_malloc(count);
     if (!hdirp) {
@@ -9226,6 +9289,23 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)
 
         namelen = strlen(hde->d_name) + 1;
         hreclen = hde->d_reclen;
+
+        /*
+         * Filter /proc/<pid>/task/ listings to hide QEMU-internal
+         * host threads (RCU, TCG) that have no guest CPUState.
+         */
+        if (filter_task) {
+            char *endp;
+            long tid = strtol(hde->d_name, &endp, 10);
+            if (tid > 0 && *endp == '\0') {
+                WITH_RCU_READ_LOCK_GUARD() {
+                    if (!is_guest_tid(tid)) {
+                        treclen = 0;
+                        continue;
+                    }
+                }
+            }
+        }
         treclen = offsetof(struct target_dirent64, d_name) + namelen;
         treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent64));
 

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] linux-user: Validate tkill/tgkill targets are guest threads
  2026-04-14 23:58 [PATCH 0/3] linux-user: Filter /proc/*/task/ and validate tkill targets Ali Raza
  2026-04-14 23:58 ` [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads Ali Raza
@ 2026-04-14 23:58 ` Ali Raza
  2026-04-14 23:58 ` [PATCH 3/3] tests/tcg: Add test for /proc/self/task/ filtering and tkill validation Ali Raza
  2 siblings, 0 replies; 6+ messages in thread
From: Ali Raza @ 2026-04-14 23:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ali Raza, morgan

The tkill and tgkill syscall handlers pass the guest-supplied TID
directly to the host kernel without checking whether it belongs to a
guest thread.  This allows a guest to send signals to QEMU-internal
host threads (RCU, TCG workers) that have no CPUState and no guest
signal handlers, which can cause hangs or disrupt QEMU operation.

Add validation that checks the target TID against the guest CPU list
before forwarding the signal to the host.  For tgkill, also verify
that the tgid matches the current process.  Return -ESRCH for TIDs
that do not correspond to any guest thread, matching the behavior a
real kernel would return for a nonexistent thread.

This complements the /proc/*/task/ filtering in the previous commit
to provide defense-in-depth: even if a guest discovers or guesses a
QEMU-internal thread TID, it cannot send signals to it.

Signed-off-by: Ali Raza (@locus-x64)
---
 linux-user/syscall.c | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index b5a912dc22..a075b9a90b 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -13449,11 +13449,41 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
 #endif
 
     case TARGET_NR_tkill:
-        return get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2)));
+    {
+        int tid = (int)arg1;
+        /*
+         * Reject signals to host threads that are not guest threads.
+         * QEMU-internal threads (RCU, TCG) share the host PID but have
+         * no CPUState and cannot handle guest-originated signals.
+         */
+        WITH_RCU_READ_LOCK_GUARD() {
+            if (!is_guest_tid(tid)) {
+                return -TARGET_ESRCH;
+            }
+        }
+        return get_errno(safe_tkill(tid, target_to_host_signal(arg2)));
+    }
 
     case TARGET_NR_tgkill:
-        return get_errno(safe_tgkill((int)arg1, (int)arg2,
+    {
+        int tgid = (int)arg1;
+        int tid = (int)arg2;
+        /*
+         * Validate that the target TID is a guest thread.  Also verify
+         * that the tgid matches our process, since all guest threads
+         * share the same host tgid.
+         */
+        if (tgid != getpid()) {
+            return -TARGET_ESRCH;
+        }
+        WITH_RCU_READ_LOCK_GUARD() {
+            if (!is_guest_tid(tid)) {
+                return -TARGET_ESRCH;
+            }
+        }
+        return get_errno(safe_tgkill(tgid, tid,
                          target_to_host_signal(arg3)));
+    }
 
 #ifdef TARGET_NR_set_robust_list
     case TARGET_NR_set_robust_list:

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] tests/tcg: Add test for /proc/self/task/ filtering and tkill validation
  2026-04-14 23:58 [PATCH 0/3] linux-user: Filter /proc/*/task/ and validate tkill targets Ali Raza
  2026-04-14 23:58 ` [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads Ali Raza
  2026-04-14 23:58 ` [PATCH 2/3] linux-user: Validate tkill/tgkill targets are guest threads Ali Raza
@ 2026-04-14 23:58 ` Ali Raza
  2 siblings, 0 replies; 6+ messages in thread
From: Ali Raza @ 2026-04-14 23:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ali Raza, morgan

Add a multiarch linux-user test that verifies:

1. /proc/self/task/ only lists guest threads -- spawns NUM_THREADS
   pthreads, reads the task directory, and asserts every entry maps
   to a known guest TID and the total count matches.

2. tkill(guest_tid, 0) succeeds for all guest threads.

3. tkill to a non-guest TID returns ESRCH.

On native Linux this test trivially passes since there are no
QEMU-internal threads.  Under QEMU user-mode, it exercises the
getdents64 filtering and tkill validation added in the previous
two commits.

Signed-off-by: Ali Raza (@locus-x64)
---
 tests/tcg/multiarch/Makefile.target         |   2 +
 tests/tcg/multiarch/linux/linux-proc-task.c | 178 ++++++++++++++++++++++++++++
 2 files changed, 180 insertions(+)

diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index 508149d57b..8d6e2af617 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -39,6 +39,8 @@ testthread: LDFLAGS+=-lpthread
 
 threadcount: LDFLAGS+=-lpthread
 
+linux-proc-task: LDFLAGS+=-lpthread
+
 signals: LDFLAGS+=-lrt -lpthread
 
 munmap-pthread: CFLAGS+=-pthread
diff --git a/tests/tcg/multiarch/linux/linux-proc-task.c b/tests/tcg/multiarch/linux/linux-proc-task.c
new file mode 100644
index 0000000000..805cfd4e47
--- /dev/null
+++ b/tests/tcg/multiarch/linux/linux-proc-task.c
@@ -0,0 +1,178 @@
+/*
+ * Test that /proc/self/task/ only lists guest threads.
+ *
+ * Under QEMU user-mode emulation, the host process may contain
+ * internal threads (RCU, TCG workers) that are not guest threads.
+ * These must be hidden from directory listings of /proc/<pid>/task/
+ * and signals to non-guest TIDs must return ESRCH.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#define _GNU_SOURCE
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define NUM_THREADS 3
+
+static pid_t guest_tids[NUM_THREADS + 1]; /* +1 for main thread */
+static int num_guest_tids;
+static pthread_barrier_t barrier;
+
+static pid_t gettid_sys(void)
+{
+    return syscall(SYS_gettid);
+}
+
+static void *thread_func(void *arg)
+{
+    int idx = (int)(intptr_t)arg;
+
+    guest_tids[idx] = gettid_sys();
+    pthread_barrier_wait(&barrier);
+
+    /* Wait for main thread to finish testing. */
+    pthread_barrier_wait(&barrier);
+    return NULL;
+}
+
+/*
+ * Read /proc/self/task/ and return the count of numeric entries
+ * (thread TIDs).  For each entry, verify it matches a known guest TID.
+ */
+static int count_and_verify_task_entries(void)
+{
+    DIR *dir;
+    struct dirent *de;
+    int count = 0;
+
+    dir = opendir("/proc/self/task");
+    assert(dir != NULL);
+
+    while ((de = readdir(dir)) != NULL) {
+        char *endp;
+        long tid;
+        int i, found;
+
+        if (de->d_name[0] == '.') {
+            continue; /* skip "." and ".." */
+        }
+
+        tid = strtol(de->d_name, &endp, 10);
+        if (*endp != '\0' || tid <= 0) {
+            continue; /* non-numeric entry */
+        }
+
+        /* Every TID in the listing must be a known guest thread. */
+        found = 0;
+        for (i = 0; i < num_guest_tids; i++) {
+            if (guest_tids[i] == (pid_t)tid) {
+                found = 1;
+                break;
+            }
+        }
+        if (!found) {
+            fprintf(stderr, "FAIL: /proc/self/task/ contains unknown TID %ld\n",
+                    tid);
+            fprintf(stderr, "  Known guest TIDs:");
+            for (i = 0; i < num_guest_tids; i++) {
+                fprintf(stderr, " %d", guest_tids[i]);
+            }
+            fprintf(stderr, "\n");
+        }
+        assert(found);
+        count++;
+    }
+
+    closedir(dir);
+    return count;
+}
+
+/*
+ * Verify that tkill(tid, 0) succeeds for all guest TIDs and that
+ * tkill to a TID that should not exist returns ESRCH.
+ */
+static void test_tkill_validation(void)
+{
+    int i, ret;
+
+    /* Signal 0 to each guest TID should succeed. */
+    for (i = 0; i < num_guest_tids; i++) {
+        ret = syscall(SYS_tkill, guest_tids[i], 0);
+        if (ret != 0) {
+            fprintf(stderr, "FAIL: tkill(%d, 0) returned %d (errno=%d)\n",
+                    guest_tids[i], ret, errno);
+        }
+        assert(ret == 0);
+    }
+
+    /*
+     * Try a TID that is very unlikely to be a guest thread.
+     * Use pid_max (typically 4194304) minus 1 as a probe.
+     * On a real kernel this would return ESRCH for a nonexistent thread;
+     * on QEMU with validation it should also return ESRCH.
+     * Skip this check if the TID happens to exist (unlikely).
+     */
+    ret = syscall(SYS_tkill, 4194303, 0);
+    if (ret == -1 && errno == ESRCH) {
+        printf("tkill to non-guest TID correctly returned ESRCH\n");
+    }
+}
+
+int main(void)
+{
+    pthread_t threads[NUM_THREADS];
+    int i, task_count;
+
+    pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1);
+
+    /* Record main thread TID. */
+    guest_tids[0] = gettid_sys();
+    num_guest_tids = 1;
+
+    /* Spawn worker threads. */
+    for (i = 0; i < NUM_THREADS; i++) {
+        int ret = pthread_create(&threads[i], NULL, thread_func,
+                                 (void *)(intptr_t)(i + 1));
+        assert(ret == 0);
+    }
+
+    /* Wait for all threads to record their TIDs. */
+    pthread_barrier_wait(&barrier);
+    num_guest_tids = NUM_THREADS + 1;
+
+    printf("Guest TIDs:");
+    for (i = 0; i < num_guest_tids; i++) {
+        printf(" %d", guest_tids[i]);
+    }
+    printf("\n");
+
+    /* Test 1: /proc/self/task/ entry count matches guest thread count. */
+    task_count = count_and_verify_task_entries();
+    printf("/proc/self/task/ entries: %d, expected: %d\n",
+           task_count, num_guest_tids);
+    assert(task_count == num_guest_tids);
+
+    /* Test 2: tkill validation. */
+    test_tkill_validation();
+
+    /* Release worker threads. */
+    pthread_barrier_wait(&barrier);
+
+    for (i = 0; i < NUM_THREADS; i++) {
+        pthread_join(threads[i], NULL);
+    }
+
+    pthread_barrier_destroy(&barrier);
+
+    printf("PASS: /proc/self/task/ filtering and tkill validation\n");
+    return EXIT_SUCCESS;
+}

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads
  2026-04-14 23:58 ` [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads Ali Raza
@ 2026-04-23 15:59   ` Helge Deller
  2026-04-24  9:26     ` Ali Raza
  0 siblings, 1 reply; 6+ messages in thread
From: Helge Deller @ 2026-04-23 15:59 UTC (permalink / raw)
  To: Ali Raza, qemu-devel; +Cc: morgan

On 4/15/26 01:58, Ali Raza wrote:
> When a guest process reads /proc/<pid>/task/ via getdents/getdents64,
> the host kernel returns directory entries for all host threads in the
> process, including QEMU-internal threads (RCU, TCG workers) that have
> no guest CPUState.  This causes problems for guest libraries like
> libcap's PSX that enumerate threads via /proc/self/task/ and send
> signals to each one -- signals sent to QEMU-internal threads are never
> handled, leading to deadlocks.
> 
> Add filtering to do_getdents() and do_getdents64() that detects when
> the fd refers to a /proc/<pid>/task/ directory and skips entries whose
> TID does not belong to a guest thread.  Guest thread TIDs are
> identified by iterating the CPU list via CPU_FOREACH(), matching the
> existing pattern used for /proc/self/stat num_threads.
> 
> This fixes the psx_test hang reported in containers using QEMU
> user-mode emulation for cross-architecture builds.
> 
> Resolves: https://github.com/AndrewGMorgan/libcap_mirror/issues/6
> Signed-off-by: Ali Raza (@locus-x64)
> ---
>   linux-user/syscall.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 80 insertions(+)
> 
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index f4b74ad350..b5a912dc22 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -9108,6 +9108,50 @@ static int host_to_target_cpu_mask(const unsigned long *host_mask,
>       return 0;
>   }
>   
> +/*
> + * Check if a directory fd refers to /proc/<pid>/task/ for the current
> + * process.  Used to filter out QEMU-internal host threads (RCU, TCG)
> + * that are not guest threads from directory listings.
> + */
> +static bool is_proc_pid_task_dir(int dirfd)
> +{
> +    char link_path[64];
> +    char link_target[PATH_MAX];
> +    ssize_t len;
> +    char expected[80];
> +    int expected_len;
> +
> +    snprintf(link_path, sizeof(link_path), "/proc/self/fd/%d", dirfd);
> +    len = readlink(link_path, link_target, sizeof(link_target) - 1);
> +    if (len < 0) {
> +        return false;
> +    }
> +    link_target[len] = '\0';
> +
> +    expected_len = snprintf(expected, sizeof(expected),
> +                            "/proc/%d/task", getpid());
> +    return strncmp(link_target, expected, expected_len) == 0
> +           && (link_target[expected_len] == '\0'
> +               || link_target[expected_len] == '/');
> +}
> +
> +/*
> + * Check if a given host TID belongs to a guest thread by looking it up
> + * in the CPU list.  Must be called under RCU read lock.
> + */
> +static bool is_guest_tid(pid_t tid)
> +{
> +    CPUState *cpu;
> +
> +    CPU_FOREACH(cpu) {
> +        TaskState *ts = get_task_state(cpu);
> +        if (ts->ts_tid == tid) {
> +            return true;
> +        }
> +    }
> +    return false;
> +}
> +
>   #ifdef TARGET_NR_getdents
>   static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)
>   {
> @@ -9116,6 +9160,7 @@ static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)
>       int hlen, hoff, toff;
>       int hreclen, treclen;
>       off_t prev_diroff = 0;
> +    bool filter_task = is_proc_pid_task_dir(dirfd);
>   
>       hdirp = g_try_malloc(count);
>       if (!hdirp) {
> @@ -9150,6 +9195,23 @@ static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)
>   
>           namelen = strlen(hde->d_name);
>           hreclen = hde->d_reclen;
> +
> +        /*
> +         * Filter /proc/<pid>/task/ listings to hide QEMU-internal
> +         * host threads (RCU, TCG) that have no guest CPUState.
> +         */
> +        if (filter_task) {
> +            char *endp;
> +            long tid = strtol(hde->d_name, &endp, 10);

I think we should user type pid_t here:
pid_t tid = strtol(...)



> +            if (tid > 0 && *endp == '\0') {
> +                WITH_RCU_READ_LOCK_GUARD() {
> +                    if (!is_guest_tid(tid)) {
> +                        treclen = 0;
> +                        continue;
> +                    }
> +                }
> +            }
> +        }
>           treclen = offsetof(struct target_dirent, d_name) + namelen + 2;
>           treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent));
>   
> @@ -9203,6 +9265,7 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)
>       int hlen, hoff, toff;
>       int hreclen, treclen;
>       off_t prev_diroff = 0;
> +    bool filter_task = is_proc_pid_task_dir(dirfd);
>   
>       hdirp = g_try_malloc(count);
>       if (!hdirp) {
> @@ -9226,6 +9289,23 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)
>   
>           namelen = strlen(hde->d_name) + 1;
>           hreclen = hde->d_reclen;
> +
> +        /*
> +         * Filter /proc/<pid>/task/ listings to hide QEMU-internal
> +         * host threads (RCU, TCG) that have no guest CPUState.
> +         */
> +        if (filter_task) {
> +            char *endp;
> +            long tid = strtol(hde->d_name, &endp, 10);

same here

> +            if (tid > 0 && *endp == '\0') {
> +                WITH_RCU_READ_LOCK_GUARD() {
> +                    if (!is_guest_tid(tid)) {
> +                        treclen = 0;
> +                        continue;
> +                    }
> +                }
> +            }
> +        }
>           treclen = offsetof(struct target_dirent64, d_name) + namelen;
>           treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent64));
>   
> 



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads
  2026-04-23 15:59   ` Helge Deller
@ 2026-04-24  9:26     ` Ali Raza
  0 siblings, 0 replies; 6+ messages in thread
From: Ali Raza @ 2026-04-24  9:26 UTC (permalink / raw)
  To: deller; +Cc: qemu-devel, laurent, pierrick.bouvier, alex.bennee, morgan

On 4/15/26, Helge Deller wrote:
>> +            char *endp;
>> +            long tid = strtol(hde->d_name, &endp, 10);
>
> I think we should use type pid_t here:
> pid_t tid = strtol(...)

Thanks for the review, Helge.

Agreed, pid_t is the correct type for a thread id. Fixed in v2 for both
do_getdents() and do_getdents64(). I also switched the parsing to
qemu_strtoi() so the whole entry name must be numeric (no trailing junk),
which avoids relying on errno/endp checks.

v2 also addresses a few other issues that came up on a closer review
(TOCTOU on the readlink-based dir check, a false EOF when an entire
host batch is filtered out, and tightening the tkill/tgkill validation
to host-process-local checks). Full changelog is in the v2 cover letter:

  https://lore.kernel.org/qemu-devel/20260424-master-v2-0-8b50b5c063ed@gmail.com/

Thanks,
Ali Raza


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-04-24  9:27 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-14 23:58 [PATCH 0/3] linux-user: Filter /proc/*/task/ and validate tkill targets Ali Raza
2026-04-14 23:58 ` [PATCH 1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads Ali Raza
2026-04-23 15:59   ` Helge Deller
2026-04-24  9:26     ` Ali Raza
2026-04-14 23:58 ` [PATCH 2/3] linux-user: Validate tkill/tgkill targets are guest threads Ali Raza
2026-04-14 23:58 ` [PATCH 3/3] tests/tcg: Add test for /proc/self/task/ filtering and tkill validation Ali Raza

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.