From nobody Thu Apr 2 22:22:19 2026 Received: from mail-pf1-f177.google.com (mail-pf1-f177.google.com [209.85.210.177]) (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 98BE133F8B4 for ; Thu, 26 Mar 2026 09:33:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774517584; cv=none; b=XUsOCICpiIYToD+Lbnj+EzP7Hj052oypkaLiB+74ZUlFHWke16L99WtG8NpdSExUrOke9ZnYjpGqd5MdSS0TOjrtSm0CfidWDKz2xC3Ybcec9THjDe+UJhWrApOj5Ht2mIFDrLFoT+ST8ZSni/XqkOk2jukAIqHHzhMYq/kQaT8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774517584; c=relaxed/simple; bh=UnFwukskcAOlCK1OJoDQ7EHbsjgmIBDEr+frkBCgbTs=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=E8qHur3GPIgb/++EX1aBVO8w2pM/sLp7FK8Auh3Jt9Y+kHbs81Y6jLT40kG6ozDYq9QkChgL2NiJ+tSB7kQTtNSsPxaHjNbQYeAvcMu9sHUrLN/dh8978VWCKREaihquHrm2B5xaLHRWeirOk3VtwvSAhcZqfBt3r8LvG7HAizg= 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=n+90c4pA; arc=none smtp.client-ip=209.85.210.177 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="n+90c4pA" Received: by mail-pf1-f177.google.com with SMTP id d2e1a72fcca58-8296d553142so405006b3a.3 for ; Thu, 26 Mar 2026 02:33:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774517583; x=1775122383; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=mAd/kp30X9xYn7MRxhcxblcceluvNmfTrFBsVsT5pOk=; b=n+90c4pAgjxfhFBdHuvpaT6QmKUwfqhHHBjF/4ZMYcmHLNqe8BMuj4bnTMaRFZVp/E IEpuRWngZuhcUgCUIva4n4ThEM7xcQCTwu068mT4XKBOEKCmbP8a8p+MmjQDJa+xn4KG HnWoU4XlaEWIrKnNLl5ipURr1zwpDfOMHcDqqex0tzSj4+R7Cr2qPWpZuLkXc7Ia2VSP FFi37OjWCHwDBBE8+g/DpvuMq7s9Q172t5ijULKEPdrmERGKPsImHmokrRfGMq82X+Un qA6pA321p3nwP5Yk0HCI9FFsqqryaw9iynciiJ5+UvG8OlVCy2ziYT5fvPH3Z6giQZBi jKQg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774517583; x=1775122383; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=mAd/kp30X9xYn7MRxhcxblcceluvNmfTrFBsVsT5pOk=; b=C2V581c75tdMV8NtHDEkGi71L6R53QBkH8th5PVxi8AlZiRNbOurMpcpgWfckK4eTx Gzpb4girSKAvQFnsgezaBEItwbw9SGig9bDVUiUcvRAgkeK0c9IO3GjBbeD+UT85Uvm/ DKwgBDYTrjmJlS1tDM/hhqKX1r0YDWH+MsYpj0eL3qCM1DYDGupy0FYRgQzb7WDrlKcf zdzwIZ05KXAORwOFCMCVDtCyTfp2jQ9MNL7fwjIpkbsx5jDBsdJQfH9ShhwacTRAWAWI FH8bkMxe/hjP828veG+iaGePr6j3PwddM9Z3kAERmmX6dRUhpiBXT+NWKm3IkDq679oJ EEPA== X-Forwarded-Encrypted: i=1; AJvYcCXRX12yRmRaOMn/QcLENfw/EkMBH8iLd6nf8A0oTj/BncIXaOANNKLCLclCY5qz7kvLMdhCfmM8D15nu7o=@vger.kernel.org X-Gm-Message-State: AOJu0Yzxbuqzp2fiIFsOWgmFpdf4SjlMSjI0pPEKEYXqc28aKxPe5k25 rz3S0jKx0fttgsixHN+MT/Yv4ILSYXmjoSsa3wYTg8AxB7Bdf1tcNWs7 X-Gm-Gg: ATEYQzwbS2Ntri6qp+/DS/cY6qXWEjCOUi7YuYpZWrkw1n/EvHyGm+ERL6v69t2r/3g Lk5qCq/GdQkbe8ighxJIymCxI/LC3lgFMl/yQo96ldlHnzCwGiC3m15XqthOqD1eM2Gm2QFyl9S hRbGac58pK5VRhoiQz4iRwS6uwJ3yb7Pad7lGoFZD2FEkPZZoyPiH/S1zoe9+znx/ClugwnDBt7 THoqkkZiRUqq2rsV8y3ieLQHoe7pumnaswumsdpFcZYidrVwDaoUQnJkVGriIDH8IR9e1+QNj8o iwlj7vYBWjlSyYOIt/M4MmZKbvX9rWviMdswNPaTaGQshtx7BE9WV/B72bQdZNdepyj2MnYmLYg X17wH015OcH3pZH7RTaf6rApBtEFcLbR7uAUA8bcbhJx8Tl0K8LpNSLW1pxsGnKZGF9thr4MxxP Sn8K1nomi+Kf0d0g0g/9GmciyzIr9v X-Received: by 2002:a05:6a00:1bcd:b0:7e8:43f5:bd4c with SMTP id d2e1a72fcca58-82c6e0cd6f1mr6353247b3a.56.1774517582610; Thu, 26 Mar 2026 02:33:02 -0700 (PDT) Received: from localhost ([111.228.63.84]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82c7d23c7a4sm2106532b3a.27.2026.03.26.02.31.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Mar 2026 02:33:02 -0700 (PDT) From: Cen Zhang To: shaggy@kernel.org Cc: jfs-discussion@lists.sourceforge.net, linux-kernel@vger.kernel.org, baijiaju1990@gmail.com, Cen Zhang Subject: [PATCH] jfs: fix tlock double-allocation race in txLock() Date: Thu, 26 Mar 2026 17:13:41 +0800 Message-Id: <20260326091341.522-1-zzzccc427@gmail.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" In txLock()'s allocateLock path, the newly allocated tlock is published (mp->lid =3D lid) only after TXN_UNLOCK(), creating a window where a concurrent txLock() call on the same metapage reads a stale mp->lid of 0, enters allocateLock itself, and creates a second tlock for the same page. This leads to two transactions holding separate tlocks for the same metapage, causing journal log corruption: both transactions log conflicting changes to the same page, and the second tlock's mp->lid assignment silently orphans the first tlock. The race is triggered in practice when two concurrent truncate operations (via jfs_setattr and ftruncate) call diWrite() for different inodes whose disk inodes share the same ipimap metapage. Both call txLock(tid, ipimap, mp, tlckINODE|tlckENTRY) concurrently. Fix this by completing all tlock initialization -- including flag, type, linelock setup, page binding (mp->lid / xtlid), and transaction/anonymous list enqueue -- while still holding TXN_LOCK, before any concurrent txLock() caller can observe the tlock. Only metapage_nohomeok() is deferred to after TXN_UNLOCK(), as it may block on folio_lock() and folio_wait_writeback() and therefore cannot execute under the TXN_LOCK spinlock. By this point the tlock is fully initialized and published, so a concurrent adopter will see a complete tlock. For anonymous transactions, the list_add_tail() to TxAnchor.anon_list is now performed under the already-held TXN_LOCK instead of dropping and re-acquiring it, which also eliminates a potential adoption-before-enqueue window. Signed-off-by: Cen Zhang --- fs/jfs/jfs_txnmgr.c | 117 ++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 54 deletions(-) diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index 51be07337c7a..3be6af49a770 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -692,29 +692,10 @@ struct tlock *txLock(tid_t tid, struct inode *ip, str= uct metapage * mp, */ tlck->tid =3D tid; =20 - TXN_UNLOCK(); - /* mark tlock for meta-data page */ - if (mp->xflag & COMMIT_PAGE) { - + if (mp->xflag & COMMIT_PAGE) tlck->flag =3D tlckPAGELOCK; - - /* mark the page dirty and nohomeok */ - metapage_nohomeok(mp); - - jfs_info("locking mp =3D 0x%p, nohomeok =3D %d tid =3D %d tlck =3D 0x%p", - mp, mp->nohomeok, tid, tlck); - - /* if anonymous transaction, and buffer is on the group - * commit synclist, mark inode to show this. This will - * prevent the buffer from being marked nohomeok for too - * long a time. - */ - if ((tid =3D=3D 0) && mp->lsn) - set_cflag(COMMIT_Synclist, ip); - } - /* mark tlock for in-memory inode */ - else + else /* mark tlock for in-memory inode */ tlck->flag =3D tlckINODELOCK; =20 if (S_ISDIR(ip->i_mode)) @@ -725,39 +706,6 @@ struct tlock *txLock(tid_t tid, struct inode *ip, stru= ct metapage * mp, /* bind the tlock and the page */ tlck->ip =3D ip; tlck->mp =3D mp; - if (dir_xtree) - jfs_ip->xtlid =3D lid; - else - mp->lid =3D lid; - - /* - * enqueue transaction lock to transaction/inode - */ - /* insert the tlock at tail of transaction tlock list */ - if (tid) { - tblk =3D tid_to_tblock(tid); - if (tblk->next) - lid_to_tlock(tblk->last)->next =3D lid; - else - tblk->next =3D lid; - tlck->next =3D 0; - tblk->last =3D lid; - } - /* anonymous transaction: - * insert the tlock at head of inode anonymous tlock list - */ - else { - tlck->next =3D jfs_ip->atlhead; - jfs_ip->atlhead =3D lid; - if (tlck->next =3D=3D 0) { - /* This inode's first anonymous transaction */ - jfs_ip->atltail =3D lid; - TXN_LOCK(); - list_add_tail(&jfs_ip->anon_inode_list, - &TxAnchor.anon_list); - TXN_UNLOCK(); - } - } =20 /* initialize type dependent area for linelock */ linelock =3D (struct linelock *) & tlck->lock; @@ -807,6 +755,67 @@ struct tlock *txLock(tid_t tid, struct inode *ip, stru= ct metapage * mp, jfs_err("UFO tlock:0x%p", tlck); } =20 + /* + * Publish the tlock (set mp->lid or jfs_ip->xtlid) and enqueue it + * while still holding TXN_LOCK. A concurrent txLock() on the same + * metapage must see the non-zero lid here; otherwise it will take + * the allocateLock path and allocate a duplicate tlock, corrupting + * the transaction journal. + */ + if (dir_xtree) + jfs_ip->xtlid =3D lid; + else + mp->lid =3D lid; + + /* + * enqueue transaction lock to transaction/inode + */ + /* insert the tlock at tail of transaction tlock list */ + if (tid) { + tblk =3D tid_to_tblock(tid); + if (tblk->next) + lid_to_tlock(tblk->last)->next =3D lid; + else + tblk->next =3D lid; + tlck->next =3D 0; + tblk->last =3D lid; + } + /* anonymous transaction: + * insert the tlock at head of inode anonymous tlock list + */ + else { + tlck->next =3D jfs_ip->atlhead; + jfs_ip->atlhead =3D lid; + if (tlck->next =3D=3D 0) { + /* This inode's first anonymous transaction */ + jfs_ip->atltail =3D lid; + list_add_tail(&jfs_ip->anon_inode_list, + &TxAnchor.anon_list); + } + } + + TXN_UNLOCK(); + + /* + * metapage_nohomeok() may block (folio_lock, folio_wait_writeback), + * so it must be called after dropping TXN_LOCK. The tlock is fully + * initialized and visible at this point. + */ + if (mp->xflag & COMMIT_PAGE) { + metapage_nohomeok(mp); + + jfs_info("locking mp =3D 0x%p, nohomeok =3D %d tid =3D %d tlck =3D 0x%p", + mp, mp->nohomeok, tid, tlck); + + /* if anonymous transaction, and buffer is on the group + * commit synclist, mark inode to show this. This will + * prevent the buffer from being marked nohomeok for too + * long a time. + */ + if ((tid =3D=3D 0) && mp->lsn) + set_cflag(COMMIT_Synclist, ip); + } + /* * update tlock vector */ --=20 2.34.1