Skip to content

Commit 0c7273e

Browse files
Dave ChinnerDarrick J. Wong
authored andcommitted
xfs: quotacheck failure can race with background inode inactivation
The background inode inactivation can attached dquots to inodes, but this can race with a foreground quotacheck failure that leads to disabling quotas and freeing the mp->m_quotainfo structure. The background inode inactivation then tries to allocate a quota, tries to dereference mp->m_quotainfo, and crashes like so: XFS (loop1): Quotacheck: Unsuccessful (Error -5): Disabling quotas. xfs filesystem being mounted at /root/syzkaller.qCVHXV/0/file0 supports timestamps until 2038 (0x7fffffff) BUG: kernel NULL pointer dereference, address: 00000000000002a8 .... CPU: 0 PID: 161 Comm: kworker/0:4 Not tainted 6.2.0-c9c3395d5e3d #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 Workqueue: xfs-inodegc/loop1 xfs_inodegc_worker RIP: 0010:xfs_dquot_alloc+0x95/0x1e0 .... Call Trace: <TASK> xfs_qm_dqread+0x46/0x440 xfs_qm_dqget_inode+0x154/0x500 xfs_qm_dqattach_one+0x142/0x3c0 xfs_qm_dqattach_locked+0x14a/0x170 xfs_qm_dqattach+0x52/0x80 xfs_inactive+0x186/0x340 xfs_inodegc_worker+0xd3/0x430 process_one_work+0x3b1/0x960 worker_thread+0x52/0x660 kthread+0x161/0x1a0 ret_from_fork+0x29/0x50 </TASK> .... Prevent this race by flushing all the queued background inode inactivations pending before purging all the cached dquots when quotacheck fails. Reported-by: Pengfei Xu <pengfei.xu@intel.com> Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
1 parent fe15c26 commit 0c7273e

File tree

1 file changed

+26
-14
lines changed

1 file changed

+26
-14
lines changed

fs/xfs/xfs_qm.c

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,15 +1321,14 @@ xfs_qm_quotacheck(
13211321

13221322
error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true,
13231323
NULL);
1324-
if (error) {
1325-
/*
1326-
* The inode walk may have partially populated the dquot
1327-
* caches. We must purge them before disabling quota and
1328-
* tearing down the quotainfo, or else the dquots will leak.
1329-
*/
1330-
xfs_qm_dqpurge_all(mp);
1331-
goto error_return;
1332-
}
1324+
1325+
/*
1326+
* On error, the inode walk may have partially populated the dquot
1327+
* caches. We must purge them before disabling quota and tearing down
1328+
* the quotainfo, or else the dquots will leak.
1329+
*/
1330+
if (error)
1331+
goto error_purge;
13331332

13341333
/*
13351334
* We've made all the changes that we need to make incore. Flush them
@@ -1363,10 +1362,8 @@ xfs_qm_quotacheck(
13631362
* and turn quotaoff. The dquots won't be attached to any of the inodes
13641363
* at this point (because we intentionally didn't in dqget_noattach).
13651364
*/
1366-
if (error) {
1367-
xfs_qm_dqpurge_all(mp);
1368-
goto error_return;
1369-
}
1365+
if (error)
1366+
goto error_purge;
13701367

13711368
/*
13721369
* If one type of quotas is off, then it will lose its
@@ -1376,7 +1373,7 @@ xfs_qm_quotacheck(
13761373
mp->m_qflags &= ~XFS_ALL_QUOTA_CHKD;
13771374
mp->m_qflags |= flags;
13781375

1379-
error_return:
1376+
error_return:
13801377
xfs_buf_delwri_cancel(&buffer_list);
13811378

13821379
if (error) {
@@ -1395,6 +1392,21 @@ xfs_qm_quotacheck(
13951392
} else
13961393
xfs_notice(mp, "Quotacheck: Done.");
13971394
return error;
1395+
1396+
error_purge:
1397+
/*
1398+
* On error, we may have inodes queued for inactivation. This may try
1399+
* to attach dquots to the inode before running cleanup operations on
1400+
* the inode and this can race with the xfs_qm_destroy_quotainfo() call
1401+
* below that frees mp->m_quotainfo. To avoid this race, flush all the
1402+
* pending inodegc operations before we purge the dquots from memory,
1403+
* ensuring that background inactivation is idle whilst we turn off
1404+
* quotas.
1405+
*/
1406+
xfs_inodegc_flush(mp);
1407+
xfs_qm_dqpurge_all(mp);
1408+
goto error_return;
1409+
13981410
}
13991411

14001412
/*

0 commit comments

Comments
 (0)