mirror of
https://github.com/versity/scoutfs.git
synced 2026-08-15 11:46:35 +00:00
Hold transaction in scoutfs_quota_mod_rule to prevent alloc corruption.
scoutfs_quota_mod_rule calls scoutfs_item_create/delete which use the transaction allocator but it never held it. Without the hold, a concurrent transaction commit can call scoutfs_alloc_init to reinitialize the allocator while dirty_alloc_blocks is in the middle of setting up the freed list block. This overwrites alloc->freed with the server's fresh (empty) state, causing a blkno mismatch BUG_ON in list_block_add. Reproduced by stressing concurrent quota add/del operations across mounts. Crashdump analysis confirms dirty_list_block COW'd a freed block (fr_old=9842, new blkno=9852) but by the time list_block_add ran, freed.ref.blkno was 0 with first_nr=0 and total_nr=0: the freed list head had been zeroed by a concurrent alloc_init. Fix by adding scoutfs_hold_trans/scoutfs_release_trans around the item modification in scoutfs_quota_mod_rule, preventing transaction commit from racing with the allocator use. Rename the 'unlock' label to 'release' since 'out' now directly does the unlock. The unlock safely handles a NULL lock. Signed-off-by: Auke Kok <auke.kok@versity.com>
This commit is contained in:
+12
-5
@@ -34,6 +34,7 @@
|
||||
#include "totl.h"
|
||||
#include "util.h"
|
||||
#include "quota.h"
|
||||
#include "trans.h"
|
||||
#include "counters.h"
|
||||
#include "scoutfs_trace.h"
|
||||
|
||||
@@ -1086,6 +1087,10 @@ int scoutfs_quota_mod_rule(struct super_block *sb, bool is_add,
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_hold_trans(sb, true);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
down_write(&qtinf->rwsem);
|
||||
|
||||
if (is_add) {
|
||||
@@ -1095,28 +1100,30 @@ int scoutfs_quota_mod_rule(struct super_block *sb, bool is_add,
|
||||
else if (ret == 0)
|
||||
ret = -EEXIST;
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
goto release;
|
||||
|
||||
rule_to_rule_val(&rv, &rule);
|
||||
ret = scoutfs_item_create(sb, &key, &rv, sizeof(rv), lock);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
goto release;
|
||||
|
||||
} else {
|
||||
ret = find_rule(sb, &rule, &key, lock) ?:
|
||||
scoutfs_item_delete(sb, &key, lock);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
goto release;
|
||||
}
|
||||
|
||||
scoutfs_quota_invalidate(sb);
|
||||
ret = 0;
|
||||
|
||||
unlock:
|
||||
release:
|
||||
up_write(&qtinf->rwsem);
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
|
||||
scoutfs_release_trans(sb);
|
||||
|
||||
out:
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
|
||||
|
||||
if (is_add)
|
||||
trace_scoutfs_quota_add_rule(sb, &rule, ret);
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user