From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f50.google.com (mail-pj1-f50.google.com [209.85.216.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 359BD1917E4 for ; Sat, 19 Oct 2024 12:09:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.50 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729339765; cv=none; b=UH4XCCcMUuEjwGPSxQdChfxix2wH+QxTNNmT9peUlH9VZZPW1kyclCjEJR2+hucFDYk9NKpHUYY2z8ZhZ4tPP3OD31gBB1UgSqce3xr+RwF9VU3S5VBaYO51A7QBVYdIdpdUwr0i8zLw5G8eFExDGIWQpDTXc2ZmoWqUjlNbXJI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729339765; c=relaxed/simple; bh=PEgd+V+AWMIjCOxK55N8oEglJBtodS+NoEZWGzsNFxQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cKKSq20xqzGwjSSpE+srBxOS0EuwcBhXpttxYbwYiN4DZFmgFkSsl7o6FchSriFzWaM3PkTXUGsTdbQrT3wNFLFPzD5DqWQFbuHiV7JVWvbr1udbQOvZ6YH+83uTDaZWvf+52jIhRt0pM7r0YNfLujtuYtOkveSjF8ZfGe/xak8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FEgaMq3h; arc=none smtp.client-ip=209.85.216.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FEgaMq3h" Received: by mail-pj1-f50.google.com with SMTP id 98e67ed59e1d1-2e56750bb0dso1064920a91.0 for ; Sat, 19 Oct 2024 05:09:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729339762; x=1729944562; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=68srU81Y5w64gB5pdYRro+Idhq4jI8D8w6Ocu0NXuts=; b=FEgaMq3hLya/sES0G2NBAcYeBVnuVtmvCbcuhg1T5y2UDw7Kw/UWtvvnOQzGyJgp33 A4yxC1q6hRx0eSADWzf3r+xb3FpE+HbyfTXglK7tVaGKHjI4+qZ3JDslkP6UW/S9k65r oU/3kafFvMDc/5E3ciCb4cSd5wggmIkxHWmsqfR7Hvxt4Id1W9LNnrsk7ZyMwQh5rUMv sZ9LROL1r8dTITAqDYDyUogtBi/c5rTBhmvuR1l3+wbzUt9idzv0QtRJLzkeW40UDtS7 fakuIEB/DAyvcpV5Ln7W4race0Gy+oYkkUrCljtufOfvJdrgvtrNCKkIoLbGa+Ig3jrw HZOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729339762; x=1729944562; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=68srU81Y5w64gB5pdYRro+Idhq4jI8D8w6Ocu0NXuts=; b=ObdD/+muBAXSN4o9vHU49HLoT/pZXCmGEPQrckzxM+KkbT/qaSVVFuCP9sZcwdPh8e RBfVwuc1hSgVySm8GICNzIV7k0lswUmphRXIvQzgTxVGPPrtM2RgE2ZEqCTWQnqG2PEF dOP2ac7ee5Tx8apJXPsIQGlZiQEv952m8dZXnySdWnnWtZpKxinGY9uHE7uCgCyZUA5e E/UZwycPL78uqE/SqFJsbo8L7MzgIzqsQhew4hskt17E2qIg6xltm3Q9ycOdPsaO0BdK WL/+sv20livbBh0O19JsJARZ0PAGCAzBOpX3iwnThHbPz0LxHMIwWpsaP4pRLIP05wg0 A+xw== X-Gm-Message-State: AOJu0YzwIb/o/lwo2cceP+Un5ETZzW9pLzrum8LSA3KryqyNHAu4wRI9 MdLf38lex6ch8nFc9Su4pYEjjtbefblyCO8VDvBKCvX6eTGgAij8o/xGJg== X-Google-Smtp-Source: AGHT+IHp5vh44ER6PjseFcu8cPdMmI2j0/z/ft+foGlXRLSUcc2bbHAMh540TpzbWkbv4qyD+I9MGg== X-Received: by 2002:a17:90a:dc18:b0:2e0:8e36:132 with SMTP id 98e67ed59e1d1-2e5616c1838mr6142309a91.3.1729339761783; Sat, 19 Oct 2024 05:09:21 -0700 (PDT) Received: from carrot.. (i118-19-49-33.s41.a014.ap.plala.or.jp. [118.19.49.33]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2e55d7c1f63sm4073152a91.17.2024.10.19.05.09.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 19 Oct 2024 05:09:20 -0700 (PDT) From: Ryusuke Konishi To: stable@vger.kernel.org, Greg Kroah-Hartman Cc: Andrew Morton , Lizhi Xu Subject: [PATCH 5.15 6.1] nilfs2: propagate directory read errors from nilfs_find_entry() Date: Sat, 19 Oct 2024 21:08:55 +0900 Message-ID: <20241019120917.4318-1-konishi.ryusuke@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <2024101851-wok-iguana-839c@gregkh> References: <2024101851-wok-iguana-839c@gregkh> Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit commit 08cfa12adf888db98879dbd735bc741360a34168 upstream. Syzbot reported that a task hang occurs in vcs_open() during a fuzzing test for nilfs2. The root cause of this problem is that in nilfs_find_entry(), which searches for directory entries, ignores errors when loading a directory page/folio via nilfs_get_folio() fails. If the filesystem images is corrupted, and the i_size of the directory inode is large, and the directory page/folio is successfully read but fails the sanity check, for example when it is zero-filled, nilfs_check_folio() may continue to spit out error messages in bursts. Fix this issue by propagating the error to the callers when loading a page/folio fails in nilfs_find_entry(). The current interface of nilfs_find_entry() and its callers is outdated and cannot propagate error codes such as -EIO and -ENOMEM returned via nilfs_find_entry(), so fix it together. Link: https://lkml.kernel.org/r/20241004033640.6841-1-konishi.ryusuke@gmail.com Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations") Signed-off-by: Ryusuke Konishi Reported-by: Lizhi Xu Closes: https://lkml.kernel.org/r/20240927013806.3577931-1-lizhi.xu@windriver.com Reported-by: syzbot+8a192e8d090fa9a31135@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8a192e8d090fa9a31135 Cc: Signed-off-by: Andrew Morton --- Please apply this patch to the stable trees indicated by the subject prefix instead of the failed patches. This patch is tailored to take page/folio conversion into account. Compiled and tested successfully. Thanks, Ryusuke Konishi fs/nilfs2/dir.c | 50 +++++++++++++++++++++++++---------------------- fs/nilfs2/namei.c | 39 ++++++++++++++++++++++++------------ fs/nilfs2/nilfs.h | 2 +- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index 36438834a0c7..b60f4fb9a405 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -331,6 +331,8 @@ static int nilfs_readdir(struct file *file, struct dir_context *ctx) * returns the page in which the entry was found, and the entry itself * (as a parameter - res_dir). Page is returned mapped and unlocked. * Entry is guaranteed to be valid. + * + * On failure, returns an error pointer and the caller should ignore res_page. */ struct nilfs_dir_entry * nilfs_find_entry(struct inode *dir, const struct qstr *qstr, @@ -358,22 +360,24 @@ nilfs_find_entry(struct inode *dir, const struct qstr *qstr, do { char *kaddr = nilfs_get_page(dir, n, &page); - if (!IS_ERR(kaddr)) { - de = (struct nilfs_dir_entry *)kaddr; - kaddr += nilfs_last_byte(dir, n) - reclen; - while ((char *) de <= kaddr) { - if (de->rec_len == 0) { - nilfs_error(dir->i_sb, - "zero-length directory entry"); - nilfs_put_page(page); - goto out; - } - if (nilfs_match(namelen, name, de)) - goto found; - de = nilfs_next_entry(de); + if (IS_ERR(kaddr)) + return ERR_CAST(kaddr); + + de = (struct nilfs_dir_entry *)kaddr; + kaddr += nilfs_last_byte(dir, n) - reclen; + while ((char *)de <= kaddr) { + if (de->rec_len == 0) { + nilfs_error(dir->i_sb, + "zero-length directory entry"); + nilfs_put_page(page); + goto out; } - nilfs_put_page(page); + if (nilfs_match(namelen, name, de)) + goto found; + de = nilfs_next_entry(de); } + nilfs_put_page(page); + if (++n >= npages) n = 0; /* next page is past the blocks we've got */ @@ -386,7 +390,7 @@ nilfs_find_entry(struct inode *dir, const struct qstr *qstr, } } while (n != start); out: - return NULL; + return ERR_PTR(-ENOENT); found: *res_page = page; @@ -431,19 +435,19 @@ struct nilfs_dir_entry *nilfs_dotdot(struct inode *dir, struct page **p) return NULL; } -ino_t nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr) +int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino) { - ino_t res = 0; struct nilfs_dir_entry *de; struct page *page; de = nilfs_find_entry(dir, qstr, &page); - if (de) { - res = le64_to_cpu(de->inode); - kunmap(page); - put_page(page); - } - return res; + if (IS_ERR(de)) + return PTR_ERR(de); + + *ino = le64_to_cpu(de->inode); + kunmap(page); + put_page(page); + return 0; } /* Releases the page */ diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 23899e0ae850..180e1ce42d21 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -55,12 +55,20 @@ nilfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct inode *inode; ino_t ino; + int res; if (dentry->d_name.len > NILFS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); - ino = nilfs_inode_by_name(dir, &dentry->d_name); - inode = ino ? nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino) : NULL; + res = nilfs_inode_by_name(dir, &dentry->d_name, &ino); + if (res) { + if (res != -ENOENT) + return ERR_PTR(res); + inode = NULL; + } else { + inode = nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino); + } + return d_splice_alias(inode, dentry); } @@ -263,10 +271,11 @@ static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry) struct page *page; int err; - err = -ENOENT; de = nilfs_find_entry(dir, &dentry->d_name, &page); - if (!de) + if (IS_ERR(de)) { + err = PTR_ERR(de); goto out; + } inode = d_inode(dentry); err = -EIO; @@ -361,10 +370,11 @@ static int nilfs_rename(struct user_namespace *mnt_userns, if (unlikely(err)) return err; - err = -ENOENT; old_de = nilfs_find_entry(old_dir, &old_dentry->d_name, &old_page); - if (!old_de) + if (IS_ERR(old_de)) { + err = PTR_ERR(old_de); goto out; + } if (S_ISDIR(old_inode->i_mode)) { err = -EIO; @@ -381,10 +391,12 @@ static int nilfs_rename(struct user_namespace *mnt_userns, if (dir_de && !nilfs_empty_dir(new_inode)) goto out_dir; - err = -ENOENT; - new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, &new_page); - if (!new_de) + new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, + &new_page); + if (IS_ERR(new_de)) { + err = PTR_ERR(new_de); goto out_dir; + } nilfs_set_link(new_dir, new_de, new_page, old_inode); nilfs_mark_inode_dirty(new_dir); new_inode->i_ctime = current_time(new_inode); @@ -438,13 +450,14 @@ static int nilfs_rename(struct user_namespace *mnt_userns, */ static struct dentry *nilfs_get_parent(struct dentry *child) { - unsigned long ino; + ino_t ino; + int res; struct inode *inode; struct nilfs_root *root; - ino = nilfs_inode_by_name(d_inode(child), &dotdot_name); - if (!ino) - return ERR_PTR(-ENOENT); + res = nilfs_inode_by_name(d_inode(child), &dotdot_name, &ino); + if (res) + return ERR_PTR(res); root = NILFS_I(d_inode(child))->i_root; diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index a1ff52265e1b..ee27bb370d77 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -233,7 +233,7 @@ static inline __u32 nilfs_mask_flags(umode_t mode, __u32 flags) /* dir.c */ extern int nilfs_add_link(struct dentry *, struct inode *); -extern ino_t nilfs_inode_by_name(struct inode *, const struct qstr *); +int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino); extern int nilfs_make_empty(struct inode *, struct inode *); extern struct nilfs_dir_entry * nilfs_find_entry(struct inode *, const struct qstr *, struct page **); -- 2.43.5