Commit 4ce718f3 authored by Stefan Roesch's avatar Stefan Roesch Committed by Andrew Morton
Browse files

mm: fix div by zero in bdi_ratio_from_pages

During testing it has been detected, that it is possible to get div by
zero error in bdi_set_min_bytes.  The error is caused by the function
bdi_ratio_from_pages().  bdi_ratio_from_pages() calls global_dirty_limits.
If the dirty threshold is 0, the div by zero is raised.  This can happen
if the root user is setting:

echo 0 > /proc/sys/vm/dirty_ratio

The following is a test case:

echo 0 > /proc/sys/vm/dirty_ratio
cd /sys/class/bdi/<device>
echo 1 > strict_limit
echo 8192 > min_bytes

==> error is raised.

The problem is addressed by returning -EINVAL if dirty_ratio or
dirty_bytes is set to 0.

[shr@devkernel.io: check for -EINVAL in bdi_set_min_bytes() and bdi_set_max_bytes()]
  Link: https://lkml.kernel.org/r/20250108014723.166637-1-shr@devkernel.io
[shr@devkernel.io: v3]
  Link: https://lkml.kernel.org/r/20250109063411.6591-1-shr@devkernel.io
Link: https://lkml.kernel.org/r/20250104012037.159386-1-shr@devkernel.io


Signed-off-by: default avatarStefan Roesch <shr@devkernel.io>
Reported-by: default avatarcheung wall <zzqq0103.hey@gmail.com>
Closes: https://lore.kernel.org/linux-mm/87pll35yd0.fsf@devkernel.io/T/#t


Acked-by: default avatarDavid Hildenbrand <david@redhat.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Qiang Zhang <zzqq0103.hey@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 59f59108
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -692,6 +692,8 @@ static unsigned long bdi_ratio_from_pages(unsigned long pages)
	unsigned long ratio;

	global_dirty_limits(&background_thresh, &dirty_thresh);
	if (!dirty_thresh)
		return -EINVAL;
	ratio = div64_u64(pages * 100ULL * BDI_RATIO_SCALE, dirty_thresh);

	return ratio;
@@ -790,13 +792,15 @@ int bdi_set_min_bytes(struct backing_dev_info *bdi, u64 min_bytes)
{
	int ret;
	unsigned long pages = min_bytes >> PAGE_SHIFT;
	unsigned long min_ratio;
	long min_ratio;

	ret = bdi_check_pages_limit(pages);
	if (ret)
		return ret;

	min_ratio = bdi_ratio_from_pages(pages);
	if (min_ratio < 0)
		return min_ratio;
	return __bdi_set_min_ratio(bdi, min_ratio);
}

@@ -809,13 +813,15 @@ int bdi_set_max_bytes(struct backing_dev_info *bdi, u64 max_bytes)
{
	int ret;
	unsigned long pages = max_bytes >> PAGE_SHIFT;
	unsigned long max_ratio;
	long max_ratio;

	ret = bdi_check_pages_limit(pages);
	if (ret)
		return ret;

	max_ratio = bdi_ratio_from_pages(pages);
	if (max_ratio < 0)
		return max_ratio;
	return __bdi_set_max_ratio(bdi, max_ratio);
}