From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 14F4E419FBB; Fri, 4 Sep 2026 06:16:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788502583; cv=none; b=Wggrmna9hlJK5KrqCVMLvVmG3DQ2AlyjGC03wol4B5Bt7xbl9x8JVgITCcE46us/Gt8JMO0EvKzXfhdpAPZJjtWR2OcokzVu/4vQEBDg4ZC2mH4gB730Y/KpTLXM85nvofG2oWdZuGR46Dt1roQL2WiORHFOp+xVBpYgSINdRTE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788502583; c=relaxed/simple; bh=RxJlNXsy1zNqwbaArJIBC4rDaLwSFv2HSQUfhEp0S5Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SEefG/IYUmkVK0E4jpOzAL15jJ7qw58lq3m4ZUW1QH0HQE3kVU2fzKVh124GL2R/1uVAwH9ruLAXMgOe2QYqddm0nS9nl2MeGcX5up17Eb7A66HgAUAxjeURycW3hR+Lg/9zZ8Q7Av7Oqte4Lum+IDEw+YFkymC+apHTYslRktk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=rgcrJgHR; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="rgcrJgHR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6F6C21F00A3E; Fri, 4 Sep 2026 06:16:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1788502582; bh=y0M9i+1Rj7VNHe8nB5WsQkZc2rIewOrtfkzyj2aUknQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=rgcrJgHRHyPTSAS69Ofakf7Zde0o55jQ3dDV2I0A5TCC2/OoU4IP9OJeb7+l3nsO5 pruxtcKkBKAlxQzNxAC3lM+YaHywj5LxqWk7ke3W84z0+7dfAnTylu2xVOd2A91ryu WyIQ9VuafURuKH1O1NO26tENbdcTesd9Ccu6tuyc= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Michael Bommarito , Chuck Lever Subject: [PATCH 6.12 257/403] lockd: pin next file across nlm_inspect_file lock-drop Date: Fri, 4 Sep 2026 07:01:00 +0200 Message-ID: <20260904045740.711367161@linuxfoundation.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260904045734.806166532@linuxfoundation.org> References: <20260904045734.806166532@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.12-stable review patch. If anyone has any objections, please let me know. ------------------ From: Michael Bommarito commit 526c49cff3f72c3ec74752016380c7567040581b upstream. nlm_traverse_files() pins the current file with f_count++ across a mutex_unlock for nlm_inspect_file(), but nothing pins the saved next pointer. A concurrent nlm_release_file() can kfree the next file during the unlock window, and the iterator dereferences freed memory on the next loop step. Pin both current and next before the lock-drop. Advance by swapping the pinned cursors at the end of each iteration so next is always held alive across the unlock. Always call nlm_file_release() after dropping the iteration pin, regardless of whether the file matched the predicate. Use nlm_file_inuse(), which does a live walk of the inode lock list, rather than the cached f_locks field, so skipped files that never ran nlm_inspect_file() are evaluated correctly. Because every file in a hash bucket is now pinned and released, files skipped by the is_failover_file predicate that have no locks, blocks, shares, or external references are deleted during traversal. The old code never evaluated skipped files for cleanup. The new behavior is intentional: such files are stale and should not persist in the table. Fixes: 01df9c5e918a ("LOCKD: Fix a deadlock in nlm_traverse_files()") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260524115527.1734251-1-michael.bommarito@gmail.com Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/lockd/svcsubs.c | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -286,12 +286,10 @@ nlm_file_inuse(struct nlm_file *file) return 0; } -static void nlm_close_files(struct nlm_file *file) +static void nlm_file_release(struct nlm_file *file) { - if (file->f_file[O_RDONLY]) - nlmsvc_ops->fclose(file->f_file[O_RDONLY]); - if (file->f_file[O_WRONLY]) - nlmsvc_ops->fclose(file->f_file[O_WRONLY]); + if (!nlm_file_inuse(file)) + nlm_delete_file(file); } /* @@ -301,32 +299,41 @@ static int nlm_traverse_files(void *data, nlm_host_match_fn_t match, int (*is_failover_file)(void *data, struct nlm_file *file)) { - struct hlist_node *next; - struct nlm_file *file; + struct nlm_file *file, *next; int i, ret = 0; mutex_lock(&nlm_file_mutex); for (i = 0; i < FILE_NRHASH; i++) { - hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) { - if (is_failover_file && !is_failover_file(data, file)) - continue; + file = hlist_entry_safe(nlm_files[i].first, + struct nlm_file, f_list); + if (file) file->f_count++; - mutex_unlock(&nlm_file_mutex); + while (file) { + /* + * Pin the next neighbour before we drop the mutex + * for nlm_inspect_file(); a concurrent + * nlm_release_file() under the same mutex would + * otherwise be free to unlink and kfree it during + * the unlock window, leaving us to dereference a + * freed slab when we walked to next afterwards. + */ + next = hlist_entry_safe(file->f_list.next, + struct nlm_file, f_list); + if (next) + next->f_count++; - /* Traverse locks, blocks and shares of this file - * and update file->f_locks count */ - if (nlm_inspect_file(data, file, match)) - ret = 1; + if (!is_failover_file || is_failover_file(data, file)) { + mutex_unlock(&nlm_file_mutex); - mutex_lock(&nlm_file_mutex); - file->f_count--; - /* No more references to this file. Let go of it. */ - if (list_empty(&file->f_blocks) && !file->f_locks - && !file->f_shares && !file->f_count) { - hlist_del(&file->f_list); - nlm_close_files(file); - kfree(file); + if (nlm_inspect_file(data, file, match)) + ret = 1; + + mutex_lock(&nlm_file_mutex); } + + file->f_count--; + nlm_file_release(file); + file = next; } } mutex_unlock(&nlm_file_mutex);