Linux cgroups development
 help / color / mirror / Atom feed
* [BUG] lib/bitmap: divide error in bitmap_fold() when sz argument is 0
@ 2026-05-28 18:25 Farhad Alemi
  2026-05-28 19:07 ` Yury Norov
  0 siblings, 1 reply; 2+ messages in thread
From: Farhad Alemi @ 2026-05-28 18:25 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Yury Norov, Waiman Long, David Hildenbrand, Rasmus Villemoes,
	cgroups, linux-mm, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4647 bytes --]

Hello,

I am reporting a divide-by-zero crash in bitmap_fold() found by syzkaller.

Summary:
bitmap_fold() at lib/bitmap.c divides by its `sz` parameter without
guarding sz != 0:

  void bitmap_fold(unsigned long *dst, const unsigned long *orig,
                   unsigned int sz, unsigned int nbits)
  {
          ...
          for_each_set_bit(oldbit, orig, nbits)
                  set_bit(oldbit % sz, dst);
  }

The call chain in the observed crash is:

  mpol_relative_nodemask()   mm/mempolicy.c
    nodes_fold(tmp, *orig, nodes_weight(*rel))
  __nodes_fold()              include/linux/nodemask.h
    bitmap_fold(dstp->bits, origp->bits, sz, nbits)
  bitmap_fold()               lib/bitmap.c

When `nodes_weight(*rel)` is 0 (i.e. the relative-nodes mask is empty),
the `sz` argument passed to bitmap_fold() is 0, and the
`oldbit % sz` expression executes a divl by zero.

Observed on:
- Linux v6.18.32-dirty (where the bug was originally found), x86_64,
  QEMU Q35
- KASAN enabled; panic_on_warn set
- The only local dirty file in my tree is drivers/tty/serial/serial_core.c,
  containing a local ttyS0 console guard for the fuzzing harness. It is
  unrelated to lib/bitmap, mm/mempolicy, or kernel/cgroup/cpuset.
- The crash fires in a cpu-hotplug kernel thread (Comm: cpuhp/1, PID 21)
  reached via sched_cpu_deactivate -> cpuset_handle_hotplug ->
  cpuset_update_tasks_nodemask -> mpol_rebind_mm -> mpol_rebind_policy
  -> mpol_rebind_nodemask -> mpol_relative_nodemask -> __nodes_fold ->
  bitmap_fold.
- Source inspection of linus/master at commit e8c2f9fdadee
  (v7.1-rc4-754-ge8c2f9fdadee) shows the buggy structure is unchanged:
  bitmap_fold() at lib/bitmap.c:718 still computes `oldbit % sz` with
  no sz != 0 guard; __nodes_fold() at include/linux/nodemask.h:365
  still forwards its sz argument; mpol_relative_nodemask() at
  mm/mempolicy.c:370 still calls nodes_fold(tmp, *orig,
  nodes_weight(*rel)). I have not re-run a reproducer against
  e8c2f9fdadee as no standalone reproducer is available yet.

Impact:
A divide-by-zero in a cpu-hotplug kernel thread context kills the
kernel:

  Oops: divide error: 0000 [#1] SMP KASAN NOPTI
  CPU: 1 UID: 0 PID: 21 Comm: cpuhp/1 Not tainted 6.18.32-dirty #1 PREEMPT(full)
  RIP: 0010:bitmap_fold+0x5e/0xb0 lib/bitmap.c:713

The crash report's code disassembly pins the trapping instruction to
`divl 0x4(%rsp)` (bytes `f7 74 24 04`) with %edx pre-zeroed by the
preceding `xor %edx,%edx` -- i.e. a 32-bit unsigned divide by the
on-stack `sz` value.

Relevant stack:

  bitmap_fold+0x5e/0xb0 lib/bitmap.c:713
  __nodes_fold include/linux/nodemask.h:369 [inline]
  mpol_relative_nodemask mm/mempolicy.c:372 [inline]
  mpol_rebind_nodemask+0x1e9/0x2d0 mm/mempolicy.c:508
  mpol_rebind_policy mm/mempolicy.c:542 [inline]
  mpol_rebind_mm+0x3ab/0x680 mm/mempolicy.c:569
  cpuset_update_tasks_nodemask+0x22e/0x340 kernel/cgroup/cpuset.c:2777
  hotplug_update_tasks kernel/cgroup/cpuset.c:3882 [inline]
  cpuset_hotplug_update_tasks kernel/cgroup/cpuset.c:3985 [inline]
  cpuset_handle_hotplug+0xe52/0x1200 kernel/cgroup/cpuset.c:4089
  cpuset_cpu_inactive kernel/sched/core.c:8377 [inline]
  sched_cpu_deactivate+0x497/0x600 kernel/sched/core.c:8493
  cpuhp_invoke_callback+0x44a/0x860 kernel/cpu.c:195
  cpuhp_thread_fun+0x40f/0x870 kernel/cpu.c:1105
  smpboot_thread_fn+0x546/0xa50 kernel/smpboot.c:160
  kthread+0x73e/0x8c0 kernel/kthread.c:432

Expected behavior:
Either bitmap_fold() should guard against sz == 0 (return early or
WARN+return), or the callers in the nodes_fold / mpol_relative_nodemask
chain should not pass a zero `sz` (e.g. short-circuit the rebind when
the relative nodemask is empty).

Reproducer:
A standalone .syz or C reproducer was not produced for this seed; the
crash fired during broader cpu/cgroup/mempolicy fuzzing. The console
report is attached as crash-report.txt.

Novelty check:
I searched the syzbot dashboard's upstream open, fixed, stable, and
invalid (per-subsystem mempolicy/mm/cgroups) namespaces, the Android
dashboard, and the marc.info linux-mm and linux-kernel archives, for
"bitmap_fold", "mpol_rebind_nodemask" + "divide error", "__nodes_fold"
+ "BUG"/"Oops", and "cpuset_handle_hotplug" + "BUG". I did not find an
exact match. The recent Jinjiang Tu series (mainline commit
3d702678f57e, "mm/mempolicy: fix mpol_rebind_nodemask() for
MPOL_F_NUMA_BALANCING") is a sibling fix in the same function but
addresses wrong-rebind logic under NUMA balancing, not the
divide-by-zero in bitmap_fold().

I appreciate your time and consideration, and I'm grateful for your
work on this subsystem. I'd be glad to test any candidate patches.

Regards,

[-- Attachment #2: crash-report.txt --]
[-- Type: text/plain, Size: 3959 bytes --]

Oops: divide error: 0000 [#1] SMP KASAN NOPTI
CPU: 1 UID: 0 PID: 21 Comm: cpuhp/1 Not tainted 6.18.32-dirty #1 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:bitmap_fold+0x5e/0xb0 lib/bitmap.c:713
Code: 31 f6 e8 a5 4e 20 fe 41 89 dc 44 89 ea 4c 89 f7 4c 89 e6 e8 84 f2 01 00 49 89 c5 44 39 eb 76 2d e8 f7 fc b9 fd 44 89 e8 31 d2 <f7> 74 24 04 89 d5 89 d0 c1 e8 06 49 8d 3c c7 be 08 00 00 00 e8 39
RSP: 0018:ffffc9000016f520 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 0000000000000040 RCX: ffff8881026a0000
RDX: 0000000000000000 RSI: 0000000000000040 RDI: ffff888126f6f218
RBP: ffffc9000016f630 R08: ffffc9000016f5a7 R09: 0000000000000000
R10: ffffc9000016f5a0 R11: fffff5200002deb5 R12: 0000000000000040
R13: 0000000000000000 R14: ffff888126f6f218 R15: ffffc9000016f5a0
FS:  0000000000000000(0000) GS:ffff8882abcc4000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fcd8c9c6fe8 CR3: 0000000192758000 CR4: 0000000000750ef0
PKRU: 55555554
Call Trace:
 <TASK>
 __nodes_fold include/linux/nodemask.h:369 [inline]
 mpol_relative_nodemask mm/mempolicy.c:372 [inline]
 mpol_rebind_nodemask+0x1e9/0x2d0 mm/mempolicy.c:508
 mpol_rebind_policy mm/mempolicy.c:542 [inline]
 mpol_rebind_mm+0x3ab/0x680 mm/mempolicy.c:569
 cpuset_update_tasks_nodemask+0x22e/0x340 kernel/cgroup/cpuset.c:2777
 hotplug_update_tasks kernel/cgroup/cpuset.c:3882 [inline]
 cpuset_hotplug_update_tasks kernel/cgroup/cpuset.c:3985 [inline]
 cpuset_handle_hotplug+0xe52/0x1200 kernel/cgroup/cpuset.c:4089
 cpuset_cpu_inactive kernel/sched/core.c:8377 [inline]
 sched_cpu_deactivate+0x497/0x600 kernel/sched/core.c:8493
 cpuhp_invoke_callback+0x44a/0x860 kernel/cpu.c:195
 cpuhp_thread_fun+0x40f/0x870 kernel/cpu.c:1105
 smpboot_thread_fn+0x546/0xa50 kernel/smpboot.c:160
 kthread+0x73e/0x8c0 kernel/kthread.c:432
 ret_from_fork+0x4b4/0xa30 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:bitmap_fold+0x5e/0xb0 lib/bitmap.c:713
Code: 31 f6 e8 a5 4e 20 fe 41 89 dc 44 89 ea 4c 89 f7 4c 89 e6 e8 84 f2 01 00 49 89 c5 44 39 eb 76 2d e8 f7 fc b9 fd 44 89 e8 31 d2 <f7> 74 24 04 89 d5 89 d0 c1 e8 06 49 8d 3c c7 be 08 00 00 00 e8 39
RSP: 0018:ffffc9000016f520 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 0000000000000040 RCX: ffff8881026a0000
RDX: 0000000000000000 RSI: 0000000000000040 RDI: ffff888126f6f218
RBP: ffffc9000016f630 R08: ffffc9000016f5a7 R09: 0000000000000000
R10: ffffc9000016f5a0 R11: fffff5200002deb5 R12: 0000000000000040
R13: 0000000000000000 R14: ffff888126f6f218 R15: ffffc9000016f5a0
FS:  0000000000000000(0000) GS:ffff8882abcc4000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fcd8c9c6fe8 CR3: 0000000192758000 CR4: 0000000000750ef0
PKRU: 55555554
----------------
Code disassembly (best guess):
   0:	31 f6                	xor    %esi,%esi
   2:	e8 a5 4e 20 fe       	call   0xfe204eac
   7:	41 89 dc             	mov    %ebx,%r12d
   a:	44 89 ea             	mov    %r13d,%edx
   d:	4c 89 f7             	mov    %r14,%rdi
  10:	4c 89 e6             	mov    %r12,%rsi
  13:	e8 84 f2 01 00       	call   0x1f29c
  18:	49 89 c5             	mov    %rax,%r13
  1b:	44 39 eb             	cmp    %r13d,%ebx
  1e:	76 2d                	jbe    0x4d
  20:	e8 f7 fc b9 fd       	call   0xfdb9fd1c
  25:	44 89 e8             	mov    %r13d,%eax
  28:	31 d2                	xor    %edx,%edx
* 2a:	f7 74 24 04          	divl   0x4(%rsp) <-- trapping instruction
  2e:	89 d5                	mov    %edx,%ebp
  30:	89 d0                	mov    %edx,%eax
  32:	c1 e8 06             	shr    $0x6,%eax
  35:	49 8d 3c c7          	lea    (%r15,%rax,8),%rdi
  39:	be 08 00 00 00       	mov    $0x8,%esi
  3e:	e8                   	.byte 0xe8
  3f:	39                   	.byte 0x39

<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-05-28 19:07 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-28 18:25 [BUG] lib/bitmap: divide error in bitmap_fold() when sz argument is 0 Farhad Alemi
2026-05-28 19:07 ` Yury Norov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox