From mboxrd@z Thu Jan 1 00:00:00 1970 From: Bob Peterson Date: Thu, 4 Jun 2020 16:15:17 -0400 (EDT) Subject: [Cluster-devel] [gfs2 patch] gfs2: fix use-after-free on transaction ail lists In-Reply-To: <839050994.31843098.1591301634432.JavaMail.zimbra@redhat.com> Message-ID: <1633443291.31843143.1591301717731.JavaMail.zimbra@redhat.com> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Hi, Before this patch, transactions could be merged into the system transaction by function gfs2_merge_trans(), but then it is not considered attached. The caller, log_refund, only sets TR_ATTACHED if the transaction is not merged. Later, in function, gfs2_trans_end, if TR_ATTACHED is not set (because it was merged) the transaction is freed by gfs2_trans_free(). However, by the time this happens, the log flush mechanism may have queued bd elements to the ail list, which means tr_ail1_list may not be empty, and the flush may forget about the bd elements altogether (memory leak) or worse, reference them after the transaction has been freed. This patch adds logic into gfs2_merge_trans() to move the merged transaction's ail lists to the sdp transaction. This prevents the use-after-free. To facilitate this, we pass sdp into the function instead of the transaction itself. Signed-off-by: Bob Peterson --- fs/gfs2/log.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 79aa0fb4984e..91e1eebc5ee0 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -1017,8 +1017,10 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) * @new: New transaction to be merged */ -static void gfs2_merge_trans(struct gfs2_trans *old, struct gfs2_trans *new) +static void gfs2_merge_trans(struct gfs2_sbd *sdp, struct gfs2_trans *new) { + struct gfs2_trans *old = sdp->sd_log_tr; + WARN_ON_ONCE(!test_bit(TR_ATTACHED, &old->tr_flags)); old->tr_num_buf_new += new->tr_num_buf_new; @@ -1030,6 +1032,11 @@ static void gfs2_merge_trans(struct gfs2_trans *old, struct gfs2_trans *new) list_splice_tail_init(&new->tr_databuf, &old->tr_databuf); list_splice_tail_init(&new->tr_buf, &old->tr_buf); + + spin_lock(&sdp->sd_ail_lock); + list_splice_tail_init(&new->tr_ail1_list, &old->tr_ail1_list); + list_splice_tail_init(&new->tr_ail2_list, &old->tr_ail2_list); + spin_unlock(&sdp->sd_ail_lock); } static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) @@ -1041,7 +1048,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) gfs2_log_lock(sdp); if (sdp->sd_log_tr) { - gfs2_merge_trans(sdp->sd_log_tr, tr); + gfs2_merge_trans(sdp, tr); } else if (tr->tr_num_buf_new || tr->tr_num_databuf_new) { gfs2_assert_withdraw(sdp, test_bit(TR_ALLOCED, &tr->tr_flags)); sdp->sd_log_tr = tr;