dm-bufio: fix a race leading to an unsigned integer underflow

The function __scan tests "count - freed <= retain_target" and then sets
"atomic_long_set(&c->need_shrink, 0);" It exits with the condition "if
(!atomic_long_read(&c->need_shrink)) break;" It may be possible that
between these two statements, another thread sets &c->need_shrink to a
non-zero value. In this case, the function __scan continues and it may
possibly the "freed" variable is larger than the "count" variable. The
expression "count - freed" underflows and the function __scan will
continue to free all the buffers, even those that are below
retain_target.

Fix this bug by immediatelly exiting if we set
"atomic_long_set(&c->need_shrink, 0)"; and don't attempt to re-read
c->need_shrink afterwards.

Fixes: 450e8dee51 ("dm bufio: improve concurrent IO performance")
Cc: stable@vger.kernel.org
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Assisted-by: Claude:claude-opus-5
This commit is contained in:
Mikulas Patocka 2026-10-12 16:09:52 +02:00
parent 1190dbeaea
commit 005d2ec572

View File

@ -2426,8 +2426,10 @@ static void __scan(struct dm_bufio_client *c)
for (l = 0; l < LIST_SIZE; l++) {
while (true) {
if (count - freed <= retain_target)
if (count - freed <= retain_target) {
atomic_long_set(&c->need_shrink, 0);
break;
}
if (!atomic_long_read(&c->need_shrink))
break;