Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2227580/?format=api
{ "id": 2227580, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2227580/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260424-master-v2-1-8b50b5c063ed@gmail.com/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/1.1/projects/14/?format=api", "name": "QEMU Development", "link_name": "qemu-devel", "list_id": "qemu-devel.nongnu.org", "list_email": "qemu-devel@nongnu.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260424-master-v2-1-8b50b5c063ed@gmail.com>", "date": "2026-04-23T21:36:38", "name": "[v2,1/3] linux-user: Filter /proc/*/task/ to hide QEMU-internal threads", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "c083155aaf1f3aaef02aa91d3ab3c1eaf9cd950b", "submitter": { "id": 93154, "url": "http://patchwork.ozlabs.org/api/1.1/people/93154/?format=api", "name": "Ali Raza", "email": "elirazamumtaz@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260424-master-v2-1-8b50b5c063ed@gmail.com/mbox/", "series": [ { "id": 501265, "url": "http://patchwork.ozlabs.org/api/1.1/series/501265/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=501265", "date": "2026-04-23T21:36:37", "name": "linux-user: Filter /proc/*/task/ and validate tkill targets", "version": 2, "mbox": "http://patchwork.ozlabs.org/series/501265/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2227580/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2227580/checks/", "tags": {}, "headers": { "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=h7QPoEsF;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)" ], "Received": [ "from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g1vNl3yMmz1xvV\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 24 Apr 2026 10:44:23 +1000 (AEST)", "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wG4ct-0004TK-El; Thu, 23 Apr 2026 20:42:35 -0400", "from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <elirazamumtaz@gmail.com>)\n id 1wG1jl-0002Zb-O6\n for qemu-devel@nongnu.org; Thu, 23 Apr 2026 17:37:29 -0400", "from mail-wm1-x330.google.com ([2a00:1450:4864:20::330])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)\n (Exim 4.90_1) (envelope-from <elirazamumtaz@gmail.com>)\n id 1wG1jj-00017U-6d\n for qemu-devel@nongnu.org; Thu, 23 Apr 2026 17:37:29 -0400", "by mail-wm1-x330.google.com with SMTP id\n 5b1f17b1804b1-4891c569cb1so7275175e9.2\n for <qemu-devel@nongnu.org>; Thu, 23 Apr 2026 14:37:26 -0700 (PDT)", "from [10.94.10.196] ([223.123.19.204])\n by smtp.gmail.com with ESMTPSA id\n 5b1f17b1804b1-48919f54572sm138755225e9.26.2026.04.23.14.37.22\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 23 Apr 2026 14:37:24 -0700 (PDT)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1776980246; x=1777585046; darn=nongnu.org;\n h=cc:to:in-reply-to:references:message-id:content-transfer-encoding\n :mime-version:subject:date:from:from:to:cc:subject:date:message-id\n :reply-to; bh=uA+3oshFCqGtJFNZ2+y4Ee11ofsxVMEKZKGagbGyBPI=;\n b=h7QPoEsFz+c550CVT9mFvjMSK8viWS6jD1E+VaX17WgYxwJJTYPx5H/f9xOWKSrEi3\n 0Fv5tEekXtRaCww8G8vOVSSjYyjzMc2u7Nep2DC+SIE5QX2crpZYJwjYtzicz4rJng+N\n ZywE+1Q6C3oPAjl4h2imZGQ9YJlMHG2C8skR33vKSvnULC5EMnUp56neqDxCMUeybm0d\n jiydv3g3HBhR9b5fLqTKEG+yDvXZKKf0X/MR/+e6escq4NtjPECiaqXpEjxCSMC6C1Ps\n 614iz5KsoKXx6SoNjfkSncz+4hF/RFrfulS6P9ybpJoyQQXCkAZlw6XE7+lOZduCrd5x\n rOXw==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776980246; x=1777585046;\n h=cc:to:in-reply-to:references:message-id:content-transfer-encoding\n :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to\n :cc:subject:date:message-id:reply-to;\n bh=uA+3oshFCqGtJFNZ2+y4Ee11ofsxVMEKZKGagbGyBPI=;\n b=eIDby3b9Na6o+8djwW4sLDEtK/N9CKSnURdL9lpfiViydOe0vGeJ7WiEGceeLVQmit\n dOxjHXE+ybvfJPRlGUiaAWyjcWfq6HGpOY3hO85Bh7TQDTtWLHb5hfYRX3sKzI0Pc4t8\n 5PQQK3n9vW0iiFIy906SYoXL6zypQeZE4pNOJ1uldc+zDsgGjSUb6L63rLo0HCu1wdpB\n hTuehaqjmSNvXn5jjOsjLnMCiFXXFaeL3iepDM2vrPKTIdte2KYSiC0Y9CncktZrzQrM\n gz/nsC68AzE1MM4/2SE1CEvb7Fll9qcAgm22uIG4JC3KZ0VMXHQvakn1FiRLi7+1q3hW\n gRJQ==", "X-Gm-Message-State": "AOJu0YwGhrEsnA/uktpQ4R1iJ2mzlU3C+j1IkTezkFK73f1LIiPFN/N1\n gVGxw9zlIrg/DfuvB2jdszi1b0S2moHvkzx/m/hk+X6oxPukEiuJG3ZCDKZBZGMD", "X-Gm-Gg": "AeBDievndWLorc8/6XhkmY5xxa8lVO8VER13NXi4rej92CLB546xc6xkbgTkWKn3gXw\n VQnqeY8wYZmVOmPuKs6NkyX4RpXY3QDZXR7n0Tp0jfAzNrNJpZdMN/8Ruq8JNrcGc00R2Ope5K0\n gDCL6qCqIxy8S7QGgvmWpx0gVwsE0KloCxCt/bpkERkLxtjsaQenx2sMO7cI8W1JtD+7dDgYwHD\n eyT3fEOIsmmYiPi7yIoSmtivXe+KPzOI9BmA6MN2DjsZBLS1OIYnI1N6dk5GsEuKaeS9IiMWUVy\n /+ZPFA6YMaUIv/IWWx6pisyptCW3qBWADLAXuJuqDDs4AmAh09Mm/We67X3xRH5e5loN2ynKcRU\n po6bwOzzWYjBPz2l3JEeG79jMdpkRXOy8GunOqzUCIQj6GhPZXWNNa2bdcBP0ezTBW0g4c1RBTk\n saYagmwfhiS/yvAoBsx8KfGa4hzg2oXg/V1GLLlfGmUQ==", "X-Received": "by 2002:a05:600c:1f82:b0:48a:5546:619e with SMTP id\n 5b1f17b1804b1-48a55466251mr92148725e9.4.1776980245251;\n Thu, 23 Apr 2026 14:37:25 -0700 (PDT)", "From": "Ali Raza <elirazamumtaz@gmail.com>", "Date": "Fri, 24 Apr 2026 02:36:38 +0500", "Subject": "[PATCH v2 1/3] linux-user: Filter /proc/*/task/ to hide\n QEMU-internal threads", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20260424-master-v2-1-8b50b5c063ed@gmail.com>", "References": "<20260424-master-v2-0-8b50b5c063ed@gmail.com>", "In-Reply-To": "<20260424-master-v2-0-8b50b5c063ed@gmail.com>", "To": "qemu-devel@nongnu.org", "Cc": "Ali Raza <elirazamumtaz@gmail.com>, Laurent Vivier <laurent@vivier.eu>,\n Pierrick Bouvier <pierrick.bouvier@linaro.org>,\n =?utf-8?q?Alex_Benn=C3=A9e?= <alex.bennee@linaro.org>, deller@gmx.de", "X-Mailer": "b4 0.15.2", "Received-SPF": "pass client-ip=2a00:1450:4864:20::330;\n envelope-from=elirazamumtaz@gmail.com; helo=mail-wm1-x330.google.com", "X-Spam_score_int": "-20", "X-Spam_score": "-2.1", "X-Spam_bar": "--", "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,\n DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001,\n RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=ham autolearn_force=no", "X-Spam_action": "no action", "X-Mailman-Approved-At": "Thu, 23 Apr 2026 20:42:33 -0400", "X-BeenThere": "qemu-devel@nongnu.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "qemu development <qemu-devel.nongnu.org>", "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>", "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>", "List-Post": "<mailto:qemu-devel@nongnu.org>", "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>", "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>", "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org", "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org" }, "content": "When a guest process reads /proc/<pid>/task/ via getdents/getdents64,\nthe host kernel returns directory entries for all host threads in the\nprocess, including QEMU-internal threads (RCU, TCG workers) that have\nno guest CPUState. This causes problems for guest libraries like\nlibcap's PSX that enumerate threads via /proc/self/task/ and send\nsignals to each one -- signals sent to QEMU-internal threads are never\nhandled, leading to deadlocks.\n\nAdd filtering to do_getdents() and do_getdents64() that detects when\nthe fd refers to a /proc/<pid>/task/ directory and skips entries whose\nTID does not belong to a guest thread. Guest thread TIDs are\nidentified by iterating the CPU list via CPU_FOREACH(), matching the\nexisting pattern used for /proc/self/stat num_threads.\n\nThis fixes the psx_test hang reported in containers using QEMU\nuser-mode emulation for cross-architecture builds.\n\nResolves: https://github.com/AndrewGMorgan/libcap_mirror/issues/6\nSigned-off-by: Ali Raza (@locus-x64)\n\n---\nChanges in v2:\n- Use pid_t instead of long for the parsed tid in do_getdents() and\n do_getdents64() (suggested by Helge Deller).\n- Replace the ad-hoc readlink-at-getdents check with fd_trans-based\n tagging at open time, eliminating a TOCTOU race and a broad-prefix\n match that also matched /proc/<pid>/task/<tid>/fd.\n- Retry sys_getdents when an entire host batch is filtered out, so\n the guest does not see a false EOF.\n- Parse the dirent TID with qemu_strtoi() instead of strtol() so\n out-of-range values cannot wrap silently into pid_t.\n---\n linux-user/fd-trans.c | 6 +\n linux-user/fd-trans.h | 18 +++\n linux-user/syscall.c | 298 +++++++++++++++++++++++++++++++++++++-------------\n 3 files changed, 244 insertions(+), 78 deletions(-)", "diff": "diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c\nindex 64dd0745d2..6bad892b78 100644\n--- a/linux-user/fd-trans.c\n+++ b/linux-user/fd-trans.c\n@@ -1881,3 +1881,9 @@ static abi_long host_to_target_data_inotify(void *buf, size_t len)\n TargetFdTrans target_inotify_trans = {\n .host_to_target_data = host_to_target_data_inotify,\n };\n+\n+/*\n+ * No callbacks: this trans entry is purely a marker to identify fds\n+ * that refer to our own /proc/<pid>/task directory. See getdents().\n+ */\n+TargetFdTrans target_proc_pid_task_trans;\ndiff --git a/linux-user/fd-trans.h b/linux-user/fd-trans.h\nindex 3bbc15fa1f..76b8c35374 100644\n--- a/linux-user/fd-trans.h\n+++ b/linux-user/fd-trans.h\n@@ -142,4 +142,22 @@ extern TargetFdTrans target_signalfd_trans;\n extern TargetFdTrans target_eventfd_trans;\n extern TargetFdTrans target_timerfd_trans;\n extern TargetFdTrans target_inotify_trans;\n+\n+/*\n+ * Marker (no callbacks) used to tag fds that refer to /proc/<pid>/task\n+ * for our own process. Used by getdents to filter out QEMU-internal\n+ * host threads (RCU, TCG workers) that have no guest CPUState.\n+ */\n+extern TargetFdTrans target_proc_pid_task_trans;\n+\n+static inline bool fd_trans_is_proc_pid_task(int fd)\n+{\n+ if (fd < 0) {\n+ return false;\n+ }\n+\n+ QEMU_LOCK_GUARD(&target_fd_trans_lock);\n+ return fd < target_fd_max\n+ && target_fd_trans[fd] == &target_proc_pid_task_trans;\n+}\n #endif\ndiff --git a/linux-user/syscall.c b/linux-user/syscall.c\nindex f4b74ad350..44f2cd851f 100644\n--- a/linux-user/syscall.c\n+++ b/linux-user/syscall.c\n@@ -8870,6 +8870,7 @@ static int do_openat2(CPUArchState *cpu_env, abi_long dirfd,\n }\n \n fd_trans_unregister(ret);\n+ maybe_tag_proc_pid_task(ret);\n unlock_user(pathname, guest_pathname, 0);\n return ret;\n }\n@@ -9108,6 +9109,58 @@ static int host_to_target_cpu_mask(const unsigned long *host_mask,\n return 0;\n }\n \n+/*\n+ * If the freshly-opened host fd refers to our own /proc/<pid>/task\n+ * directory, register a marker on it via fd_trans so getdents() can\n+ * later filter out QEMU-internal host threads (RCU, TCG workers) that\n+ * have no guest CPUState. Tagging at open time (rather than at\n+ * getdents time) avoids TOCTOU on the fd's resolved path and lets us\n+ * piggy-back on the existing fd_trans infrastructure for dup/close.\n+ */\n+static void maybe_tag_proc_pid_task(int fd)\n+{\n+ char link_path[64];\n+ char link_target[PATH_MAX];\n+ char expected[64];\n+ ssize_t len;\n+ int expected_len;\n+\n+ if (fd < 0) {\n+ return;\n+ }\n+\n+ snprintf(link_path, sizeof(link_path), \"/proc/self/fd/%d\", fd);\n+ len = readlink(link_path, link_target, sizeof(link_target) - 1);\n+ if (len < 0) {\n+ return;\n+ }\n+ link_target[len] = '\\0';\n+\n+ expected_len = snprintf(expected, sizeof(expected),\n+ \"/proc/%d/task\", getpid());\n+ if (expected_len > 0 && (ssize_t)expected_len == len\n+ && memcmp(link_target, expected, expected_len) == 0) {\n+ fd_trans_register(fd, &target_proc_pid_task_trans);\n+ }\n+}\n+\n+/*\n+ * Check if a given host TID belongs to a guest thread by looking it up\n+ * in the CPU list. Must be called under RCU read lock.\n+ */\n+static bool is_guest_tid(pid_t tid)\n+{\n+ CPUState *cpu;\n+\n+ CPU_FOREACH(cpu) {\n+ TaskState *ts = get_task_state(cpu);\n+ if (ts->ts_tid == tid) {\n+ return true;\n+ }\n+ }\n+ return false;\n+}\n+\n #ifdef TARGET_NR_getdents\n static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)\n {\n@@ -9116,78 +9169,123 @@ static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)\n int hlen, hoff, toff;\n int hreclen, treclen;\n off_t prev_diroff = 0;\n+ bool filter_task = fd_trans_is_proc_pid_task(dirfd);\n+ bool stop = false;\n \n hdirp = g_try_malloc(count);\n if (!hdirp) {\n return -TARGET_ENOMEM;\n }\n \n-#ifdef EMULATE_GETDENTS_WITH_GETDENTS\n- hlen = sys_getdents(dirfd, hdirp, count);\n-#else\n- hlen = sys_getdents64(dirfd, hdirp, count);\n-#endif\n-\n- hlen = get_errno(hlen);\n- if (is_error(hlen)) {\n- return hlen;\n- }\n-\n tdirp = lock_user(VERIFY_WRITE, arg2, count, 0);\n if (!tdirp) {\n return -TARGET_EFAULT;\n }\n \n- for (hoff = toff = 0; hoff < hlen; hoff += hreclen, toff += treclen) {\n+ toff = 0;\n+ /*\n+ * Loop until we either produce a visible target entry, hit a\n+ * real EOF (host returns 0), or hit an error. Without this\n+ * retry, a host batch consisting entirely of filtered entries\n+ * would be reported to the guest as EOF.\n+ */\n+ while (!stop) {\n #ifdef EMULATE_GETDENTS_WITH_GETDENTS\n- struct linux_dirent *hde = hdirp + hoff;\n+ hlen = sys_getdents(dirfd, hdirp, count);\n #else\n- struct linux_dirent64 *hde = hdirp + hoff;\n+ hlen = sys_getdents64(dirfd, hdirp, count);\n #endif\n- struct target_dirent *tde = tdirp + toff;\n- int namelen;\n- uint8_t type;\n+ hlen = get_errno(hlen);\n+ if (is_error(hlen)) {\n+ unlock_user(tdirp, arg2, 0);\n+ return hlen;\n+ }\n+ if (hlen == 0) {\n+ break; /* real EOF */\n+ }\n \n- namelen = strlen(hde->d_name);\n- hreclen = hde->d_reclen;\n- treclen = offsetof(struct target_dirent, d_name) + namelen + 2;\n- treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent));\n+ for (hoff = 0; hoff < hlen; hoff += hreclen, toff += treclen) {\n+#ifdef EMULATE_GETDENTS_WITH_GETDENTS\n+ struct linux_dirent *hde = hdirp + hoff;\n+#else\n+ struct linux_dirent64 *hde = hdirp + hoff;\n+#endif\n+ struct target_dirent *tde = tdirp + toff;\n+ int namelen;\n+ uint8_t type;\n+\n+ namelen = strlen(hde->d_name);\n+ hreclen = hde->d_reclen;\n \n- if (toff + treclen > count) {\n /*\n- * If the host struct is smaller than the target struct, or\n- * requires less alignment and thus packs into less space,\n- * then the host can return more entries than we can pass\n- * on to the guest.\n+ * Filter /proc/<pid>/task/ listings to hide QEMU-internal\n+ * host threads (RCU, TCG) that have no guest CPUState.\n */\n- if (toff == 0) {\n- toff = -TARGET_EINVAL; /* result buffer is too small */\n+ if (filter_task) {\n+ int tid_int;\n+ if (qemu_strtoi(hde->d_name, NULL, 10, &tid_int) == 0\n+ && tid_int > 0) {\n+ pid_t tid = (pid_t)tid_int;\n+ bool drop;\n+\n+ WITH_RCU_READ_LOCK_GUARD() {\n+ drop = !is_guest_tid(tid);\n+ }\n+ if (drop) {\n+ treclen = 0;\n+ continue;\n+ }\n+ }\n+ }\n+ treclen = offsetof(struct target_dirent, d_name) + namelen + 2;\n+ treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent));\n+\n+ if (toff + treclen > count) {\n+ /*\n+ * If the host struct is smaller than the target struct, or\n+ * requires less alignment and thus packs into less space,\n+ * then the host can return more entries than we can pass\n+ * on to the guest.\n+ */\n+ if (toff == 0) {\n+ toff = -TARGET_EINVAL; /* result buffer is too small */\n+ stop = true;\n+ break;\n+ }\n+ /*\n+ * Return what we have, resetting the file pointer to the\n+ * location of the first record not returned.\n+ */\n+ lseek(dirfd, prev_diroff, SEEK_SET);\n+ stop = true;\n break;\n }\n+\n+ prev_diroff = hde->d_off;\n+ tde->d_ino = tswapal(hde->d_ino);\n+ tde->d_off = tswapal(hde->d_off);\n+ tde->d_reclen = tswap16(treclen);\n+ memcpy(tde->d_name, hde->d_name, namelen + 1);\n+\n /*\n- * Return what we have, resetting the file pointer to the\n- * location of the first record not returned.\n+ * The getdents type is in what was formerly a padding byte at the\n+ * end of the structure.\n */\n- lseek(dirfd, prev_diroff, SEEK_SET);\n- break;\n+#ifdef EMULATE_GETDENTS_WITH_GETDENTS\n+ type = *((uint8_t *)hde + hreclen - 1);\n+#else\n+ type = hde->d_type;\n+#endif\n+ *((uint8_t *)tde + treclen - 1) = type;\n }\n \n- prev_diroff = hde->d_off;\n- tde->d_ino = tswapal(hde->d_ino);\n- tde->d_off = tswapal(hde->d_off);\n- tde->d_reclen = tswap16(treclen);\n- memcpy(tde->d_name, hde->d_name, namelen + 1);\n-\n /*\n- * The getdents type is in what was formerly a padding byte at the\n- * end of the structure.\n+ * If we produced any visible target entry, return; otherwise\n+ * loop again to fetch more host entries (avoid false EOF).\n */\n-#ifdef EMULATE_GETDENTS_WITH_GETDENTS\n- type = *((uint8_t *)hde + hreclen - 1);\n-#else\n- type = hde->d_type;\n-#endif\n- *((uint8_t *)tde + treclen - 1) = type;\n+ if (toff != 0) {\n+ break;\n+ }\n }\n \n unlock_user(tdirp, arg2, toff);\n@@ -9203,57 +9301,99 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)\n int hlen, hoff, toff;\n int hreclen, treclen;\n off_t prev_diroff = 0;\n+ bool filter_task = fd_trans_is_proc_pid_task(dirfd);\n+ bool stop = false;\n \n hdirp = g_try_malloc(count);\n if (!hdirp) {\n return -TARGET_ENOMEM;\n }\n \n- hlen = get_errno(sys_getdents64(dirfd, hdirp, count));\n- if (is_error(hlen)) {\n- return hlen;\n- }\n-\n tdirp = lock_user(VERIFY_WRITE, arg2, count, 0);\n if (!tdirp) {\n return -TARGET_EFAULT;\n }\n \n- for (hoff = toff = 0; hoff < hlen; hoff += hreclen, toff += treclen) {\n- struct linux_dirent64 *hde = hdirp + hoff;\n- struct target_dirent64 *tde = tdirp + toff;\n- int namelen;\n+ toff = 0;\n+ /*\n+ * Loop until we either produce a visible target entry, hit a\n+ * real EOF (host returns 0), or hit an error. Without this\n+ * retry, a host batch consisting entirely of filtered entries\n+ * would be reported to the guest as EOF.\n+ */\n+ while (!stop) {\n+ hlen = get_errno(sys_getdents64(dirfd, hdirp, count));\n+ if (is_error(hlen)) {\n+ unlock_user(tdirp, arg2, 0);\n+ return hlen;\n+ }\n+ if (hlen == 0) {\n+ break; /* real EOF */\n+ }\n+\n+ for (hoff = 0; hoff < hlen; hoff += hreclen, toff += treclen) {\n+ struct linux_dirent64 *hde = hdirp + hoff;\n+ struct target_dirent64 *tde = tdirp + toff;\n+ int namelen;\n \n- namelen = strlen(hde->d_name) + 1;\n- hreclen = hde->d_reclen;\n- treclen = offsetof(struct target_dirent64, d_name) + namelen;\n- treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent64));\n+ namelen = strlen(hde->d_name) + 1;\n+ hreclen = hde->d_reclen;\n \n- if (toff + treclen > count) {\n /*\n- * If the host struct is smaller than the target struct, or\n- * requires less alignment and thus packs into less space,\n- * then the host can return more entries than we can pass\n- * on to the guest.\n+ * Filter /proc/<pid>/task/ listings to hide QEMU-internal\n+ * host threads (RCU, TCG) that have no guest CPUState.\n */\n- if (toff == 0) {\n- toff = -TARGET_EINVAL; /* result buffer is too small */\n+ if (filter_task) {\n+ int tid_int;\n+ if (qemu_strtoi(hde->d_name, NULL, 10, &tid_int) == 0\n+ && tid_int > 0) {\n+ pid_t tid = (pid_t)tid_int;\n+ bool drop;\n+\n+ WITH_RCU_READ_LOCK_GUARD() {\n+ drop = !is_guest_tid(tid);\n+ }\n+ if (drop) {\n+ treclen = 0;\n+ continue;\n+ }\n+ }\n+ }\n+ treclen = offsetof(struct target_dirent64, d_name) + namelen;\n+ treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent64));\n+\n+ if (toff + treclen > count) {\n+ /*\n+ * If the host struct is smaller than the target struct, or\n+ * requires less alignment and thus packs into less space,\n+ * then the host can return more entries than we can pass\n+ * on to the guest.\n+ */\n+ if (toff == 0) {\n+ toff = -TARGET_EINVAL; /* result buffer is too small */\n+ stop = true;\n+ break;\n+ }\n+ /*\n+ * Return what we have, resetting the file pointer to the\n+ * location of the first record not returned.\n+ */\n+ lseek(dirfd, prev_diroff, SEEK_SET);\n+ stop = true;\n break;\n }\n- /*\n- * Return what we have, resetting the file pointer to the\n- * location of the first record not returned.\n- */\n- lseek(dirfd, prev_diroff, SEEK_SET);\n- break;\n+\n+ prev_diroff = hde->d_off;\n+ tde->d_ino = tswap64(hde->d_ino);\n+ tde->d_off = tswap64(hde->d_off);\n+ tde->d_reclen = tswap16(treclen);\n+ tde->d_type = hde->d_type;\n+ memcpy(tde->d_name, hde->d_name, namelen);\n }\n \n- prev_diroff = hde->d_off;\n- tde->d_ino = tswap64(hde->d_ino);\n- tde->d_off = tswap64(hde->d_off);\n- tde->d_reclen = tswap16(treclen);\n- tde->d_type = hde->d_type;\n- memcpy(tde->d_name, hde->d_name, namelen);\n+ if (toff != 0) {\n+ break;\n+ }\n }\n \n unlock_user(tdirp, arg2, toff);\n@@ -9732,6 +9872,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,\n target_to_host_bitmask(arg2, fcntl_flags_tbl),\n arg3, true));\n fd_trans_unregister(ret);\n+ maybe_tag_proc_pid_task(ret);\n unlock_user(p, arg1, 0);\n return ret;\n #endif\n@@ -9742,6 +9883,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,\n target_to_host_bitmask(arg3, fcntl_flags_tbl),\n arg4, true));\n fd_trans_unregister(ret);\n+ maybe_tag_proc_pid_task(ret);\n unlock_user(p, arg2, 0);\n return ret;\n case TARGET_NR_openat2:\n", "prefixes": [ "v2", "1/3" ] }