Skip to content

Commit dfd2bf4

Browse files
2045geminiliu-song-6
authored andcommitted
md/raid5: fix atomicity violation in raid5_cache_count
In raid5_cache_count(): if (conf->max_nr_stripes < conf->min_nr_stripes) return 0; return conf->max_nr_stripes - conf->min_nr_stripes; The current check is ineffective, as the values could change immediately after being checked. In raid5_set_cache_size(): ... conf->min_nr_stripes = size; ... while (size > conf->max_nr_stripes) conf->min_nr_stripes = conf->max_nr_stripes; ... Due to intermediate value updates in raid5_set_cache_size(), concurrent execution of raid5_cache_count() and raid5_set_cache_size() may lead to inconsistent reads of conf->max_nr_stripes and conf->min_nr_stripes. The current checks are ineffective as values could change immediately after being checked, raising the risk of conf->min_nr_stripes exceeding conf->max_nr_stripes and potentially causing an integer overflow. This possible bug is found by an experimental static analysis tool developed by our team. This tool analyzes the locking APIs to extract function pairs that can be concurrently executed, and then analyzes the instructions in the paired functions to identify possible concurrency bugs including data races and atomicity violations. The above possible bug is reported when our tool analyzes the source code of Linux 6.2. To resolve this issue, it is suggested to introduce local variables 'min_stripes' and 'max_stripes' in raid5_cache_count() to ensure the values remain stable throughout the check. Adding locks in raid5_cache_count() fails to resolve atomicity violations, as raid5_set_cache_size() may hold intermediate values of conf->min_nr_stripes while unlocked. With this patch applied, our tool no longer reports the bug, with the kernel configuration allyesconfig for x86_64. Due to the lack of associated hardware, we cannot test the patch in runtime testing, and just verify it according to the code logic. Fixes: edbe83a ("md/raid5: allow the stripe_cache to grow and shrink.") Cc: [email protected] Signed-off-by: Gui-Dong Han <[email protected]> Reviewed-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Song Liu <[email protected]>
1 parent ecbd8eb commit dfd2bf4

File tree

1 file changed

+8
-6
lines changed

1 file changed

+8
-6
lines changed

drivers/md/raid5.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2412,7 +2412,7 @@ static int grow_one_stripe(struct r5conf *conf, gfp_t gfp)
24122412
atomic_inc(&conf->active_stripes);
24132413

24142414
raid5_release_stripe(sh);
2415-
conf->max_nr_stripes++;
2415+
WRITE_ONCE(conf->max_nr_stripes, conf->max_nr_stripes + 1);
24162416
return 1;
24172417
}
24182418

@@ -2707,7 +2707,7 @@ static int drop_one_stripe(struct r5conf *conf)
27072707
shrink_buffers(sh);
27082708
free_stripe(conf->slab_cache, sh);
27092709
atomic_dec(&conf->active_stripes);
2710-
conf->max_nr_stripes--;
2710+
WRITE_ONCE(conf->max_nr_stripes, conf->max_nr_stripes - 1);
27112711
return 1;
27122712
}
27132713

@@ -6820,7 +6820,7 @@ raid5_set_cache_size(struct mddev *mddev, int size)
68206820
if (size <= 16 || size > 32768)
68216821
return -EINVAL;
68226822

6823-
conf->min_nr_stripes = size;
6823+
WRITE_ONCE(conf->min_nr_stripes, size);
68246824
mutex_lock(&conf->cache_size_mutex);
68256825
while (size < conf->max_nr_stripes &&
68266826
drop_one_stripe(conf))
@@ -6832,7 +6832,7 @@ raid5_set_cache_size(struct mddev *mddev, int size)
68326832
mutex_lock(&conf->cache_size_mutex);
68336833
while (size > conf->max_nr_stripes)
68346834
if (!grow_one_stripe(conf, GFP_KERNEL)) {
6835-
conf->min_nr_stripes = conf->max_nr_stripes;
6835+
WRITE_ONCE(conf->min_nr_stripes, conf->max_nr_stripes);
68366836
result = -ENOMEM;
68376837
break;
68386838
}
@@ -7388,11 +7388,13 @@ static unsigned long raid5_cache_count(struct shrinker *shrink,
73887388
struct shrink_control *sc)
73897389
{
73907390
struct r5conf *conf = shrink->private_data;
7391+
int max_stripes = READ_ONCE(conf->max_nr_stripes);
7392+
int min_stripes = READ_ONCE(conf->min_nr_stripes);
73917393

7392-
if (conf->max_nr_stripes < conf->min_nr_stripes)
7394+
if (max_stripes < min_stripes)
73937395
/* unlikely, but not impossible */
73947396
return 0;
7395-
return conf->max_nr_stripes - conf->min_nr_stripes;
7397+
return max_stripes - min_stripes;
73967398
}
73977399

73987400
static struct r5conf *setup_conf(struct mddev *mddev)

0 commit comments

Comments
 (0)