From patchwork Fri Aug 16 18:07:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bethany Jamison X-Patchwork-Id: 1973365 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ubuntu.com (client-ip=185.125.189.65; helo=lists.ubuntu.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=patchwork.ozlabs.org) Received: from lists.ubuntu.com (lists.ubuntu.com [185.125.189.65]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Wls313HFXz20GF for ; Sat, 17 Aug 2024 05:08:28 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=lists.ubuntu.com) by lists.ubuntu.com with esmtp (Exim 4.86_2) (envelope-from ) id 1sf2JA-0001JF-Ne; Fri, 16 Aug 2024 19:08:20 +0000 Received: from smtp-relay-internal-0.internal ([10.131.114.225] helo=smtp-relay-internal-0.canonical.com) by lists.ubuntu.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1sf2J8-0001HS-Ce for kernel-team@lists.ubuntu.com; Fri, 16 Aug 2024 19:08:18 +0000 Received: from mail-qv1-f71.google.com (mail-qv1-f71.google.com [209.85.219.71]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 430E53F48A for ; Fri, 16 Aug 2024 19:08:18 +0000 (UTC) Received: by mail-qv1-f71.google.com with SMTP id 6a1803df08f44-6bf56d9b781so22398366d6.3 for ; Fri, 16 Aug 2024 12:08:18 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1723835297; x=1724440097; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=3ZXOt2ltQxmJV5pxkOOqauX7CDx62/hcVqUT2RnuEVQ=; b=IKpquyropr3Hv2KlhV0GCDJw5Fq+TRcGnpVQA9QRAsAdDBB69GIozsI5zF9k1jG7Pv CwpnnQ93g2mVyDm/6D1QYOKtDDrn7RgcBbqNboyPbAqdauQ2lToSpn3YQRoM3OB/z1gW bHLAussFVXrPKjguZKUZdxaJQngec/SRdWND1SERTEEW/xc1KF7eOI64gM6gCKQ9hnM+ fi8zHHY9fm9YGTWBiI+5zelC+KXq0pJ9d4dkn4eCKKIJ6a4xIAPY5B8LVfARiqQqHmZP H1aBn51/RYKUZ8Q0cEo1sKFnthi9n7oWC+EEs19zxT8PP+eX2fZnlcgfM6ZkTaIBX1MY EcoA== X-Gm-Message-State: AOJu0YzQZajgsBGZE0nQ16mfAnTRNdYEq4DTbrkVgsy72p1UIbLf+CAw rpaO2ocowfbcTjqkIVJM1VgqnJaJDDaymX4buKp3doQ7VbAu9M4LxjnOGuYg2K2CSiahCjQfeGD VlKJ+5i6p7YNf9ehI+l8GxKs8doi3vHVfa1nFtbInGn2iCx/WFwt+BFujSG4DdPeBn6DHoDv1Zd kmckAMQpKppZzf5fs= X-Received: by 2002:a05:6214:3d98:b0:6b7:ad32:3815 with SMTP id 6a1803df08f44-6bf7cd9e2a5mr47312396d6.14.1723835296897; Fri, 16 Aug 2024 12:08:16 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFWCtyecm/vpUaS6SYzb8ltrFp+yeKy9+01Oh0h9Bk22+vqgXoiZbnIjaQQfp4wPPLMP12cpQ== X-Received: by 2002:a05:6214:3d98:b0:6b7:ad32:3815 with SMTP id 6a1803df08f44-6bf7cd9e2a5mr47311966d6.14.1723835296200; Fri, 16 Aug 2024 12:08:16 -0700 (PDT) Received: from smtp.gmail.com ([2001:67c:1562:8007::aac:48f9]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-6bf763cb74fsm18116026d6.112.2024.08.16.12.08.15 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Aug 2024 12:08:15 -0700 (PDT) From: Bethany Jamison To: kernel-team@lists.ubuntu.com Subject: [SRU][F][PATCH 2/2] gfs2: Fix potential glock use-after-free on unmount Date: Fri, 16 Aug 2024 13:07:54 -0500 Message-Id: <20240816180754.22733-5-bethany.jamison@canonical.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240816180754.22733-1-bethany.jamison@canonical.com> References: <20240816180754.22733-1-bethany.jamison@canonical.com> MIME-Version: 1.0 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Andreas Gruenbacher When a DLM lockspace is released and there ares still locks in that lockspace, DLM will unlock those locks automatically. Commit fb6791d100d1b started exploiting this behavior to speed up filesystem unmount: gfs2 would simply free glocks it didn't want to unlock and then release the lockspace. This didn't take the bast callbacks for asynchronous lock contention notifications into account, which remain active until until a lock is unlocked or its lockspace is released. To prevent those callbacks from accessing deallocated objects, put the glocks that should not be unlocked on the sd_dead_glocks list, release the lockspace, and only then free those glocks. As an additional measure, ignore unexpected ast and bast callbacks if the receiving glock is dead. Fixes: fb6791d100d1b ("GFS2: skip dlm_unlock calls in unmount") Signed-off-by: Andreas Gruenbacher Cc: David Teigland (backported from commit d98779e687726d8f8860f1c54b5687eec5f63a73) [bjamison: context conflicts with neighboring lines] CVE-2024-38570 Signed-off-by: Bethany Jamison --- fs/gfs2/glock.c | 35 ++++++++++++++++++++++++++++++++--- fs/gfs2/glock.h | 1 + fs/gfs2/incore.h | 1 + fs/gfs2/lock_dlm.c | 12 +++++++++++- fs/gfs2/ops_fstype.c | 1 + fs/gfs2/super.c | 3 --- 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index fffe3f6e2cf22..0d2d17d65b7e3 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -133,19 +133,46 @@ static void gfs2_glock_dealloc(struct rcu_head *rcu) } } -void gfs2_glock_free(struct gfs2_glock *gl) +static void __gfs2_glock_free(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; - BUG_ON(atomic_read(&gl->gl_revokes)); rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms); smp_mb(); wake_up_glock(gl); call_rcu(&gl->gl_rcu, gfs2_glock_dealloc); +} + +void gfs2_glock_free(struct gfs2_glock *gl) { + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + __gfs2_glock_free(gl); + if (atomic_dec_and_test(&sdp->sd_glock_disposal)) + wake_up(&sdp->sd_kill_wait); +} + +void gfs2_glock_free_later(struct gfs2_glock *gl) { + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + spin_lock(&lru_lock); + list_add(&gl->gl_lru, &sdp->sd_dead_glocks); + spin_unlock(&lru_lock); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) wake_up(&sdp->sd_kill_wait); } +static void gfs2_free_dead_glocks(struct gfs2_sbd *sdp) +{ + struct list_head *list = &sdp->sd_dead_glocks; + + while(!list_empty(list)) { + struct gfs2_glock *gl; + + gl = list_first_entry(list, struct gfs2_glock, gl_lru); + list_del_init(&gl->gl_lru); + __gfs2_glock_free(gl); + } +} + /** * gfs2_glock_hold() - increment reference count on glock * @gl: The glock to hold @@ -1737,6 +1764,8 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) wait_event_timeout(sdp->sd_kill_wait, atomic_read(&sdp->sd_glock_disposal) == 0, HZ * 600); + gfs2_lm_unmount(sdp); + gfs2_free_dead_glocks(sdp); glock_hash_walk(dump_glock_func, sdp); } diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index b8adaf80e4c5e..50341f938695f 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -240,6 +240,7 @@ extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip); extern void gfs2_glock_thaw(struct gfs2_sbd *sdp); extern void gfs2_glock_add_to_lru(struct gfs2_glock *gl); extern void gfs2_glock_free(struct gfs2_glock *gl); +void gfs2_glock_free_later(struct gfs2_glock *gl); extern int __init gfs2_glock_init(void); extern void gfs2_glock_exit(void); diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 0cb967ada92e5..13adc981b18d7 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -844,6 +844,7 @@ struct gfs2_sbd { struct gfs2_holder sd_freeze_gh; atomic_t sd_freeze_state; struct mutex sd_freeze_mutex; + struct list_head sd_dead_glocks; char sd_fsname[GFS2_FSNAME_LEN + 3 * sizeof(int) + 2]; char sd_table_name[GFS2_FSNAME_LEN]; diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 94c290a333a0a..87fe09809c1bd 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -116,6 +116,11 @@ static void gdlm_ast(void *arg) struct gfs2_glock *gl = arg; unsigned ret = gl->gl_state; + /* If the glock is dead, we only react to a dlm_unlock() reply. */ + if (__lockref_is_dead(&gl->gl_lockref) && + gl->gl_lksb.sb_status != -DLM_EUNLOCK) + return; + gfs2_update_reply_times(gl); BUG_ON(gl->gl_lksb.sb_flags & DLM_SBF_DEMOTED); @@ -164,6 +169,9 @@ static void gdlm_bast(void *arg, int mode) { struct gfs2_glock *gl = arg; + if (__lockref_is_dead(&gl->gl_lockref)) + return; + switch (mode) { case DLM_LOCK_EX: gfs2_glock_cb(gl, LM_ST_UNLOCKED); @@ -282,6 +290,8 @@ static void gdlm_put_lock(struct gfs2_glock *gl) struct lm_lockstruct *ls = &sdp->sd_lockstruct; int error; + BUG_ON(!__lockref_is_dead(&gl->gl_lockref)); + if (gl->gl_lksb.sb_lkid == 0) { gfs2_glock_free(gl); return; @@ -301,7 +311,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl) if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) && !gl->gl_lksb.sb_lvbptr) { - gfs2_glock_free(gl); + gfs2_glock_free_later(gl); return; } diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 63754ff56482d..aec95d9053e65 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -139,6 +139,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) init_waitqueue_head(&sdp->sd_log_flush_wait); atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN); mutex_init(&sdp->sd_freeze_mutex); + INIT_LIST_HEAD(&sdp->sd_dead_glocks); return sdp; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 09bb630185952..39db9397191a0 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -693,10 +693,7 @@ static void gfs2_put_super(struct super_block *sb) gfs2_gl_hash_clear(sdp); truncate_inode_pages_final(&sdp->sd_aspace); gfs2_delete_debugfs_file(sdp); - /* Unmount the locking protocol */ - gfs2_lm_unmount(sdp); - /* At this point, we're through participating in the lockspace */ gfs2_sys_fs_del(sdp); free_sbd(sdp); }