Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2225254/?format=api
{ "id": 2225254, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2225254/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20260420174349.3804322-1-adhemerval.zanella@linaro.org/", "project": { "id": 41, "url": "http://patchwork.ozlabs.org/api/1.1/projects/41/?format=api", "name": "GNU C Library", "link_name": "glibc", "list_id": "libc-alpha.sourceware.org", "list_email": "libc-alpha@sourceware.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260420174349.3804322-1-adhemerval.zanella@linaro.org>", "date": "2026-04-20T17:43:20", "name": "[v2] io: Fix silent readdir failures in ftw/nftw (BZ 33085)", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "866599a0b9ae444aac1a459bd5265e51f714784f", "submitter": { "id": 66065, "url": "http://patchwork.ozlabs.org/api/1.1/people/66065/?format=api", "name": "Adhemerval Zanella", "email": "adhemerval.zanella@linaro.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20260420174349.3804322-1-adhemerval.zanella@linaro.org/mbox/", "series": [ { "id": 500653, "url": "http://patchwork.ozlabs.org/api/1.1/series/500653/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=500653", "date": "2026-04-20T17:43:20", "name": "[v2] io: Fix silent readdir failures in ftw/nftw (BZ 33085)", "version": 2, "mbox": "http://patchwork.ozlabs.org/series/500653/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2225254/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2225254/checks/", "tags": {}, "headers": { "Return-Path": "<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "libc-alpha@sourceware.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "libc-alpha@sourceware.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=cklplUX5;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=cklplUX5", "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=linaro.org", "sourceware.org; spf=pass smtp.mailfrom=linaro.org", "server2.sourceware.org;\n arc=none smtp.remote-ip=2607:f8b0:4864:20::e35" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fztCS16Vkz1yD4\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 03:44:20 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 29CFA4BA23CF\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 20 Apr 2026 17:44:18 +0000 (GMT)", "from mail-vs1-xe35.google.com (mail-vs1-xe35.google.com\n [IPv6:2607:f8b0:4864:20::e35])\n by sourceware.org (Postfix) with ESMTPS id 55C924BA23CF\n for <libc-alpha@sourceware.org>; Mon, 20 Apr 2026 17:43:56 +0000 (GMT)", "by mail-vs1-xe35.google.com with SMTP id\n ada2fe7eead31-60fa5eb3ee1so1082078137.2\n for <libc-alpha@sourceware.org>; Mon, 20 Apr 2026 10:43:56 -0700 (PDT)", "from mandiga.. ([2804:1b3:a7c3:d5d0:f11f:bea9:f78d:1be4])\n by smtp.gmail.com with ESMTPSA id\n a1e0cc1a2514c-9589098226dsm4885178241.5.2026.04.20.10.43.52\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 20 Apr 2026 10:43:53 -0700 (PDT)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org 29CFA4BA23CF", "OpenDKIM Filter v2.11.0 sourceware.org 55C924BA23CF" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 55C924BA23CF", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 55C924BA23CF", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776707036; cv=none;\n b=YBC4gPouvWpm7+hWvACddHHB0cs9kW0unoRGXSygfjFYcnUyiouw7tEcXAm98bzExSRdBakSEPO9NwtjlPvYfVV84KW5gAzLw5w2KbCffOUEPNLSf3E24isA1c989ilFQHtdiyOnSuZdzdCEoLBeU/DpBkem1BJNIWIhUKWXdb4=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776707036; c=relaxed/simple;\n bh=jWe5BkkbdnDOE8pRDwQBm1UWIE4KsN3fO8JYP11uWsM=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=OSuWosYi3245c2AsBfUzA/3HtD9gnFqUZ1z04AadHfKf+s3W87cxDpjJ15NG7t4wFJTdhXoe9hm6sYt5pMe+V6mwrO4Fiemg/V814yQfPW+EQbXlMFqvvDptFJr/CJICMTIGIwZZ9biHfCG07weFz2KV0qpfxX4CoWhJ1rcsS6I=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=linaro.org; s=google; t=1776707034; x=1777311834; darn=sourceware.org;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:from:to:cc:subject:date:message-id:reply-to;\n bh=XrZ/nZZgiF/JrjO/sqRpgtdTPQOoKFgsOjAmhOtLjc8=;\n b=cklplUX5Cu9/Zcedq70mZqMTEf/fmnKyvlNHGIfJxDvu2T9X00XUZKDMwD78jhGddM\n ebEEm3+oBSYKPFuN62R5Fj/xQUAdg4HL4I/yMB+DOPYGOg/rYLHzrtL2DQQ9HFGJTrpc\n xnPwBwpLsFOlDimitGfI4/F5/q7YRPWhUXB90DesGzsoUf3ZEmjeNEDxOtJniglMLqJO\n nKf8KzcEMG9lVdkUtuf7xGdborxYXDUKlNscLAMhRi9MYgmSDVSZzy0TJHQe5gRzO4lq\n 0tcoeYsc629beOdOWI0NU+3FnYZFQRQJJsHxh2SjUIYXSbsv5g0anEKbc+8hMERAyrml\n gPGA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776707034; x=1777311834;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=XrZ/nZZgiF/JrjO/sqRpgtdTPQOoKFgsOjAmhOtLjc8=;\n b=pEIE5xGG2zdFIiSY7H6lu+jk2PoOuyU1DDk10v/MmYaoM7SO8NAQydgbo2TBsQ5ukl\n hz+EINmsps39RaYleCpsrNCbDCqwoKScp0RxRndCbhRQ8nntBgz1B+P8zbos8qbZMijt\n x5ZC4/3edkToR93eimVJYo9SxTI6BqqfPXLEZ3V78IepV/eojWj5+q3pXdfahHxBVgXa\n Lx2+/6FK8EdnLbUeHL7TWSYdsHRlSuyLe3uYerVt+dCE0niBwJweH7zutUduMy6O/Pop\n dYrXEMuvRJosjJgWEilNvlFxNC1eNEhwg6jtrKtkd3wQiM3HYTzcJGsehDg7k4cKzaWj\n ERJg==", "X-Gm-Message-State": "AOJu0YwDOTeOUw+PMFsAVuLJfXDXSZ/e1KP8fFbiXGyh5Gf5r11NH/Yl\n ubzpHQoy4FPctcOHbaD7W/Fz7TUTNFWJ9nZHlR+DlB4sWwvmZnZUXpFyGk3TY0xTAEhN1dQYal6\n z/H8g", "X-Gm-Gg": "AeBDiesbKHQ6/wJt/hv1/mpRFi1/JMjQfBrTWAs9h63sL2nIgy2/DCGImFu97kDOmoj\n /M5QT51SMgVprhG0qIu2CyL6hQ8I0jSeA9n3Fk+B01DTB+I9eWNpNE8+tfeQzfOtjVqbbAP99tK\n 7AwtSWnBuJypPQltZlBeXvysRTrfrqIwezB2eaL3AdfyLo7FbnIQdBh/w+YYKwVxH9r1NGGrTrD\n WDuxHepA8sSGbXq8Jj/+ps0bbW+WD7xSCXRYHjSmU/wtMzRECKq43mDWrs/KcwIEcsD+sV/lW7M\n AfZVvds/pYqNp6yp2jzR/kxP6KoyXfcgnPoRIZ14pKepec+erbsb3+yji1JPqnEc73MeXhii2Wi\n Jer7V7aeT4JVidQoTfneYSXJH+kViKPWa44ap/UhjBOAxPhXWqCQqEBcutYFDfA6fP2BVxX9REH\n aLxwMCfdhmQj6Vq8HvWgmx8+UfNUfRkWsLiJtGa3Nxvps1bQdoAnZ0xHXl", "X-Received": "by 2002:a05:6102:a4c:b0:604:e063:4fa8 with SMTP id\n ada2fe7eead31-616f7e6deb8mr5391953137.26.1776707034173;\n Mon, 20 Apr 2026 10:43:54 -0700 (PDT)", "From": "Adhemerval Zanella <adhemerval.zanella@linaro.org>", "To": "libc-alpha@sourceware.org", "Cc": "DJ Delorie <dj@redhat.com>", "Subject": "[PATCH v2] io: Fix silent readdir failures in ftw/nftw (BZ 33085)", "Date": "Mon, 20 Apr 2026 14:43:20 -0300", "Message-ID": "<20260420174349.3804322-1-adhemerval.zanella@linaro.org>", "X-Mailer": "git-send-email 2.43.0", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libc-alpha@sourceware.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Libc-alpha mailing list <libc-alpha.sourceware.org>", "List-Unsubscribe": "<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>", "List-Archive": "<https://sourceware.org/pipermail/libc-alpha/>", "List-Post": "<mailto:libc-alpha@sourceware.org>", "List-Help": "<mailto:libc-alpha-request@sourceware.org?subject=help>", "List-Subscribe": "<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>", "Errors-To": "libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org" }, "content": "The {n}ftw functions fails to distinguish between the end of a directory\nstream and an read error. The existing implementation treated all\nNULL returns as the end of the stream, silently swallowing file system\nor I/O errors and incorrectly reporting a successful traversal.\n\nThis patch fixes the issue in two places within io/ftw.c:\n\n1. In open_dir_stream (the fallback loop used when the file\n descriptor limit is exhausted and directory entries must be cached).\n\n2. In ftw_dir (the main directory reading loop, specifically within\n FTW_STATE_STREAM_LOOP).\n\nThe testcasuse uses the FUSE libsupport to trigger getdents failure\nin this two readdir calls.\n\nChecked on x86_64-linux-gnu and i686-linux-gnu.\n--\nChanges from v1:\n- Add extra free on open_dir_stream early exit.\n- Fixed typos.\n---\n io/Makefile | 1 +\n io/ftw-common.c | 20 ++++-\n io/tst-ftw-bz33085.c | 195 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 214 insertions(+), 2 deletions(-)\n create mode 100644 io/tst-ftw-bz33085.c", "diff": "diff --git a/io/Makefile b/io/Makefile\nindex 2a5b4dcb65..151dd36418 100644\n--- a/io/Makefile\n+++ b/io/Makefile\n@@ -202,6 +202,7 @@ tests := \\\n tst-fts-newflags \\\n tst-ftw-bz26353 \\\n tst-ftw-bz28126 \\\n+ tst-ftw-bz33085 \\\n tst-ftw-lnk \\\n tst-futimens \\\n tst-futimes \\\ndiff --git a/io/ftw-common.c b/io/ftw-common.c\nindex 07df0ab25a..17349a284d 100644\n--- a/io/ftw-common.c\n+++ b/io/ftw-common.c\n@@ -246,8 +246,19 @@ open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)\n \t struct dirent64 *d;\n \t size_t actsize = 0;\n \n-\t while ((d = __readdir64 (st)) != NULL)\n+\t while (1)\n \t {\n+\t errno = 0;\n+\t d = __readdir64 (st);\n+\t if (d == NULL)\n+\t\t{\n+\t\t if (errno != 0)\n+\t\t {\n+\t\t free (buf);\n+\t\t return -1;\n+\t\t }\n+\t\t break;\n+\t\t}\n \t size_t this_len = NAMLEN (d);\n \t if (actsize + this_len + 2 >= bufsize)\n \t\t{\n@@ -607,6 +618,7 @@ ftw_dir (struct ftw_data *data, const struct STRUCT_STAT *st)\n \t continue;\n \t }\n \n+\t errno = 0;\n \t struct dirent64 *d = __readdir64 (frame->dir.stream);\n \t if (d != NULL)\n \t {\n@@ -631,7 +643,11 @@ ftw_dir (struct ftw_data *data, const struct STRUCT_STAT *st)\n \t\t}\n \t }\n \t else\n-\t frame->state = FTW_STATE_CLEANUP;\n+\t {\n+\t frame->state = FTW_STATE_CLEANUP;\n+\t if (errno != 0)\n+\t\tresult = -1;\n+\t }\n \t}\n else if (frame->state == FTW_STATE_CONTENT_LOOP)\n \t{\ndiff --git a/io/tst-ftw-bz33085.c b/io/tst-ftw-bz33085.c\nnew file mode 100644\nindex 0000000000..c318b5a9f9\n--- /dev/null\n+++ b/io/tst-ftw-bz33085.c\n@@ -0,0 +1,195 @@\n+/* Test if nftw correctly handles readdir errors.\n+ Copyright (C) 2026 Free Software Foundation, Inc.\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+\n+#include <ftw.h>\n+#include <dirent.h>\n+#include <errno.h>\n+#include <string.h>\n+#include <stdio.h>\n+#include <stdbool.h>\n+#include <sys/stat.h>\n+\n+#include <support/check.h>\n+#include <support/fuse.h>\n+#include <support/support.h>\n+\n+/* The test stresses the readdir calls from nftw, where failures should not\n+ handled as end of stream. The first is at 'ftw_dir'\n+ (FTW_STATE_STREAM_LOOP) for the default entries read. The another one is\n+ at open_dir_stream where it is triggered when there is a file description\n+ exaustion and the code must close an existing stream to make room for the\n+ new subdirectory stream. */\n+\n+static _Atomic bool readdir_triggered = false;\n+\n+static void\n+fuse_thread (struct support_fuse *f, void *closure)\n+{\n+ struct fuse_in_header *inh;\n+\n+ while ((inh = support_fuse_next (f)) != NULL)\n+ {\n+ switch (inh->opcode)\n+ {\n+ case FUSE_GETATTR:\n+ {\n+ /* We need to respond for both the root (1) and dir1 (2) */\n+ if (inh->nodeid == 1 || inh->nodeid == 2)\n+ {\n+ struct fuse_attr_out out = { 0 };\n+ out.attr_valid = 60;\n+ out.attr.ino = inh->nodeid;\n+ out.attr.mode = S_IFDIR | 0755;\n+ out.attr.nlink = 3; /* Force nftw to look for subdirs */\n+ support_fuse_reply (f, &out, sizeof (out));\n+ }\n+ else\n+ support_fuse_reply_error (f, ENOENT);\n+ break;\n+ }\n+\n+ case FUSE_LOOKUP:\n+ {\n+ const char *name = (const char *) (inh + 1);\n+ if (strcmp (name, \"dir1\") == 0)\n+ {\n+ struct fuse_entry_out out = { 0 };\n+ out.nodeid = 2;\n+ out.attr_valid = 60;\n+ out.entry_valid = 60;\n+ out.attr.ino = 2;\n+ out.attr.mode = S_IFDIR | 0755;\n+ out.attr.nlink = 2;\n+ support_fuse_reply (f, &out, sizeof (out));\n+ }\n+ else\n+ support_fuse_reply_error (f, ENOENT);\n+ break;\n+ }\n+\n+ case FUSE_OPENDIR:\n+ {\n+ struct fuse_open_out out = { 0 };\n+ out.fh = inh->nodeid;\n+ support_fuse_reply (f, &out, sizeof (out));\n+ break;\n+ }\n+\n+ case FUSE_READDIR:\n+ {\n+ const struct fuse_read_in *rin = support_fuse_cast (READ, inh);\n+\n+ if (inh->nodeid == 1) /* Reading the Root directory */\n+ {\n+ if (rin->offset == 0)\n+ {\n+ /* First readdir, this happens in FTW_STATE_STREAM_LOOP.\n+ We yield \"dir1\", prompting nftw to descend. */\n+ char buf[256] = { 0 };\n+ struct fuse_dirent *d = (struct fuse_dirent *) buf;\n+\n+ d->ino = 2;\n+ d->off = 1;\n+ d->type = DT_DIR;\n+ d->namelen = 4;\n+ strcpy (d->name, \"dir1\");\n+\n+ size_t d_size =\n+\t\t FUSE_DIRENT_ALIGN (sizeof (struct fuse_dirent)\n+\t\t\t\t\t + d->namelen);\n+ support_fuse_reply (f, buf, d_size);\n+ }\n+ else\n+ {\n+\t\t /* Second readdir, this ONLY happens inside\n+\t\t open_dir_stream() when nftw tries to cache the\n+\t\t remaining entries before closing the stream to descend\n+\t\t into \"dir1\". */\n+ readdir_triggered = true;\n+ support_fuse_reply_error (f, EIO);\n+ }\n+ }\n+ else\n+ /* Subdirectory logic (shouldn't be reached in this test) */\n+ support_fuse_reply_empty (f);\n+ break;\n+ }\n+\n+ case FUSE_READDIRPLUS:\n+ support_fuse_reply_error (f, EIO);\n+ break;\n+\n+ case FUSE_ACCESS:\n+ case FUSE_RELEASEDIR:\n+ support_fuse_reply_empty (f);\n+ break;\n+\n+ default:\n+ support_fuse_reply_error (f, EIO);\n+ }\n+ }\n+}\n+\n+static int\n+nftw_cb (const char *fpath, const struct stat *sb, int typeflag,\n+\t struct FTW *ftwbuf)\n+{\n+ return 0;\n+}\n+\n+static int\n+do_test (void)\n+{\n+ support_fuse_init ();\n+ struct support_fuse *f = support_fuse_mount (fuse_thread, NULL);\n+\n+ {\n+ /* This forces nftw to immediately exhaust its FD limit when it tries\n+ to descend into 'dir1', forcing it into the open_dir_stream fallback\n+ loop. */\n+ errno = 0;\n+ readdir_triggered = false;\n+ int ret = nftw (support_fuse_mountpoint (f), nftw_cb, 1, FTW_PHYS);\n+ /* Assert that we successfully hit the caching __readdir64 block */\n+ TEST_VERIFY (readdir_triggered);\n+\n+ /* Assert that nftw correctly aborted and propagated the EIO */\n+ TEST_COMPARE (ret, -1);\n+ TEST_COMPARE (errno, EIO);\n+ }\n+\n+ {\n+ /* Use a high descriptor count (10) so nftw doesn't fall back to\n+ caching */\n+ errno = 0;\n+ readdir_triggered = false;\n+ int ret = nftw (support_fuse_mountpoint (f), nftw_cb, 10, FTW_PHYS);\n+ /* Assert that the second readdir in the main loop was actually hit */\n+ TEST_VERIFY (readdir_triggered);\n+\n+ /* Assert that nftw correctly aborts and propagates the EIO\n+ This will fail until the `#if 0` block in ftw.c is patched) */\n+ TEST_COMPARE (ret, -1);\n+ TEST_COMPARE (errno, EIO);\n+ }\n+\n+ support_fuse_unmount (f);\n+ return 0;\n+}\n+\n+#include <support/test-driver.c>\n", "prefixes": [ "v2" ] }