lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAM0EoMmVchdXPrmnFObON4kf3FOz2SbjFptRmqTev3GtkxWGvQ@mail.gmail.com>
Date: Thu, 29 Jan 2026 08:40:45 -0500
From: Jamal Hadi Salim <jhs@...atatu.com>
To: GangMin Kim <km.kim1503@...il.com>
Cc: davem@...emloft.net, Eric Dumazet <edumazet@...gle.com>, jiri@...nulli.us, 
	kuba@...nel.org, netdev@...r.kernel.org, pabeni@...hat.com, 
	stephen@...workplumber.org, xiyou.wangcong@...il.com, horms@...nel.org, 
	linux-kernel@...r.kernel.org, syzkaller@...glegroups.com
Subject: Re: [BUG] divide error in tabledist

On Wed, Jan 28, 2026 at 11:20 PM GangMin Kim <km.kim1503@...il.com> wrote:
>
> Dear Linux kernel developers and maintainers,
> Using a modified version of syzkaller, I identified a new bug and
> refined the PoC, and the bug-related information is attached
> below.Please let me know if you need any further information.
>

Thanks for the report. I will take a closer look later today.

cheers,
jamal

> Summary
> In netem, when jitter is set to 0x80000000, a divide error occurs when
> tabledist is processed during the subsequent netem_enqueue operation.
>
> Keywords
> - net/sched
>
> Kernel Info
> Version: (Output of /proc/version)
> - Linux version 6.19.0-rc7
> Commit: (Git hash if applicable)
> - 63804fed149a6750ffd28610c5c1c98cce6bd377
>
> Description(Root Cause)
> ```
> static int netem_change(struct Qdisc *sch, struct nlattr *opt,
> struct netlink_ext_ack *extack)
> {
> struct netem_sched_data *q = qdisc_priv(sch);
> struct nlattr *tb[TCA_NETEM_MAX + 1];
> struct disttable *delay_dist = NULL;
> struct disttable *slot_dist = NULL;
> struct tc_netem_qopt *qopt;
> struct clgstate old_clg;
> int old_loss_model = CLG_RANDOM;
> int ret;
>
> qopt = nla_data(opt);
> ...
> sch->limit = qopt->limit;
>
> q->latency = PSCHED_TICKS2NS(qopt->latency);
> q->jitter = PSCHED_TICKS2NS(qopt->jitter); // [1]
> q->limit = qopt->limit;
> q->gap = qopt->gap;
> q->counter = 0;
> q->loss = qopt->loss;
>
> ret = check_netem_in_tree(sch, qopt->duplicate, extack); // [2]
> if (ret)
> goto unlock;
> ...
> /* capping jitter to the range acceptable by tabledist() */
> q->jitter = min_t(s64, abs(q->jitter), INT_MAX); // [3]
> ...
> unlock:
> sch_tree_unlock(sch);
>
> table_free:
> dist_free(delay_dist);
> dist_free(slot_dist);
> return ret;
> }
> ```
> When qopt->jitter is 0x6e000000, at [1] q->jitter is set to
> 0x1b80000000. If netem is already configured and a change operation is
> attempted but fails at [2], the q values are modified but the process
> at [3] is not executed. Therefore, q->jitter can be set to a value
> exceeding INT_MAX.
>
> ```
> static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> struct sk_buff **to_free)
> {
> struct netem_sched_data *q = qdisc_priv(sch);
> /* We don't fill cb now as skb_unshare() may invalidate it */
> struct netem_skb_cb *cb;
> struct sk_buff *skb2 = NULL;
> struct sk_buff *segs = NULL;
> unsigned int prev_len = qdisc_pkt_len(skb);
> int count = 1;
> ...
> if (q->gap == 0 || /* not doing reordering */
>     q->counter < q->gap - 1 || /* inside last reordering gap */
>     q->reorder < get_crandom(&q->reorder_cor, &q->prng)) {
> u64 now;
> s64 delay;
>
> delay = tabledist(q->latency, q->jitter, // [4]
>   &q->delay_cor, &q->prng, q->delay_dist);
>
> ...
> }
> ...
> return NET_XMIT_SUCCESS;
> }
>
> static s64 tabledist(s64 mu, s32 sigma,
>      struct crndstate *state,
>      struct prng *prng,
>      const struct disttable *dist)
> {
> s64 x;
> long t;
> u32 rnd;
>
> if (sigma == 0)
> return mu;
>
> rnd = get_crandom(state, prng);
>
> /* default uniform distribution */
> if (dist == NULL)
> return ((rnd % (2 * (u32)sigma)) + mu) - sigma; // [5]
> ...
> return  x / NETEM_DIST_SCALE + (sigma / NETEM_DIST_SCALE) * t + mu;
> }
> ```
> Subsequently, in netem_enqueue(), the previously set value is passed
> at [4], and in tabledist(), sigma is set to 0x80000000. This causes
> the calculation at [5] to become 2 * 0x80000000, which overflows to 0,
> resulting in a divide-by-zero error.
>
> Kasan Report
> Oops: divide error: 0000 [#1] SMP KASAN NOPTI
> CPU: 0 UID: 0 PID: 332 Comm: test Not tainted 6.19.0-rc7 #8 PREEMPT(full)
> Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS
> 1.16.3-debian-1.16.3-2 04/01/2014
> RIP: 0010:tabledist.part.0+0x21e/0x290 net/sched/sch_netem.c:345
> Code: 4c 01 f0 48 c1 e8 20 41 89 45 00 41 89 c6 e8 49 d1 9a fd 48 85
> db 0f 85 78 fe ff ff e8 3b d1 9a fd 8d 4c 2d 00 44 89 f0 31 d2 <f7> f1
> 49 29 ec 49 01 d4 e9 2a ff ff ff e8 20 d1 9a fd 48 81 eb 00
> RSP: 0018:ffff88810f61f1f8 EFLAGS: 00010246
> RAX: 00000000aecf3ad7 RBX: 0000000000000000 RCX: 0000000000000000
> RDX: 0000000000000000 RSI: 0000000080000000 RDI: ffff88810900da7c
> RBP: ffffffff80000000 R08: 0000000000000000 R09: ffffed10233895cc
> R10: ffff888119c4ae67 R11: 0000000000000000 R12: 0000000001000300
> R13: ffff88810900da3c R14: 00000000aecf3ad7 R15: ffff88810900da40
> FS:  00007f2531e11540(0000) GS:ffff88834ad89000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00007f2531cfe1c0 CR3: 00000001215fe000 CR4: 0000000000750ef0
> PKRU: 55555554
> Call Trace:
>  <TASK>
>  tabledist net/sched/sch_netem.c:559 [inline]
>  netem_enqueue+0x4f4/0x3180 net/sched/sch_netem.c:559
>  qdisc_enqueue include/net/sch_generic.h:887 [inline]
>  qfq_enqueue+0x46f/0x13c0 net/sched/sch_qfq.c:1256
>  dev_qdisc_enqueue+0x45/0x160 net/core/dev.c:4147
>  __dev_xmit_skb net/core/dev.c:4262 [inline]
>  __dev_queue_xmit+0x1eea/0x3060 net/core/dev.c:4798
>  dev_queue_xmit include/linux/netdevice.h:3381 [inline]
>  neigh_hh_output include/net/neighbour.h:540 [inline]
>  neigh_output include/net/neighbour.h:554 [inline]
>  ip_finish_output2+0xdd2/0x1710 net/ipv4/ip_output.c:237
>  __ip_finish_output.part.0+0x182/0x2f0 net/ipv4/ip_output.c:315
>  __ip_finish_output net/ipv4/ip_output.c:444 [inline]
>  ip_finish_output net/ipv4/ip_output.c:325 [inline]
>  NF_HOOK_COND include/linux/netfilter.h:307 [inline]
>  ip_output+0x288/0x510 net/ipv4/ip_output.c:438
>  dst_output include/net/dst.h:464 [inline]
>  ip_local_out net/ipv4/ip_output.c:131 [inline]
>  ip_send_skb+0x160/0x1b0 net/ipv4/ip_output.c:1508
>  udp_send_skb+0x6d6/0x1000 net/ipv4/udp.c:1195
>  udp_sendmsg+0x1463/0x1eb0 net/ipv4/udp.c:1484
>  inet_sendmsg+0xfa/0x140 net/ipv4/af_inet.c:859
>  sock_sendmsg_nosec net/socket.c:727 [inline]
>  __sock_sendmsg net/socket.c:742 [inline]
>  sock_write_iter+0x493/0x5b0 net/socket.c:1195
>  new_sync_write fs/read_write.c:593 [inline]
>  vfs_write+0x657/0xd30 fs/read_write.c:686
>  ksys_write+0x1b2/0x200 fs/read_write.c:738
>  do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
>  do_syscall_64+0xa4/0x320 arch/x86/entry/syscall_64.c:94
>  entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7f2531d27513
> Code: 8b 15 81 29 0e 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f
> 1f 00 64 8b 04 25 18 00 00 00 85 c0 75 14 b8 01 00 00 00 0f 05 <48> 3d
> 00 f0 ff ff 77 55 c3 0f 1f 40 00 48 83 ec 28 48 89 54 24 18
> RSP: 002b:00007ffe09e5d858 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
> RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f2531d27513
> RDX: 0000000000000040 RSI: 00007ffe09e5d890 RDI: 0000000000000004
> RBP: 00007ffe09e5d8f0 R08: 0000000000000004 R09: 0000000000020000
> R10: 00007ffe09e5d87c R11: 0000000000000246 R12: 000055c28686a100
> R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
>  </TASK>
> Modules linked in:
> ---[ end trace 0000000000000000 ]---
> RIP: 0010:tabledist.part.0+0x21e/0x290 net/sched/sch_netem.c:345
> Code: 4c 01 f0 48 c1 e8 20 41 89 45 00 41 89 c6 e8 49 d1 9a fd 48 85
> db 0f 85 78 fe ff ff e8 3b d1 9a fd 8d 4c 2d 00 44 89 f0 31 d2 <f7> f1
> 49 29 ec 49 01 d4 e9 2a ff ff ff e8 20 d1 9a fd 48 81 eb 00
> RSP: 0018:ffff88810f61f1f8 EFLAGS: 00010246
> RAX: 00000000aecf3ad7 RBX: 0000000000000000 RCX: 0000000000000000
> RDX: 0000000000000000 RSI: 0000000080000000 RDI: ffff88810900da7c
> RBP: ffffffff80000000 R08: 0000000000000000 R09: ffffed10233895cc
> R10: ffff888119c4ae67 R11: 0000000000000000 R12: 0000000001000300
> R13: ffff88810900da3c R14: 00000000aecf3ad7 R15: ffff88810900da40
> FS:  00007f2531e11540(0000) GS:ffff88834ad89000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00007f2531cfe1c0 CR3: 00000001215fe000 CR4: 0000000000750ef0
> PKRU: 55555554
> ----------------
> Code disassembly (best guess):
>    0: 4c 01 f0              add    %r14,%rax
>    3: 48 c1 e8 20          shr    $0x20,%rax
>    7: 41 89 45 00          mov    %eax,0x0(%r13)
>    b: 41 89 c6              mov    %eax,%r14d
>    e: e8 49 d1 9a fd        call   0xfd9ad15c
>   13: 48 85 db              test   %rbx,%rbx
>   16: 0f 85 78 fe ff ff    jne    0xfffffe94
>   1c: e8 3b d1 9a fd        call   0xfd9ad15c
>   21: 8d 4c 2d 00          lea    0x0(%rbp,%rbp,1),%ecx
>   25: 44 89 f0              mov    %r14d,%eax
>   28: 31 d2                xor    %edx,%edx
> * 2a: f7 f1                div    %ecx <-- trapping instruction
>   2c: 49 29 ec              sub    %rbp,%r12
>   2f: 49 01 d4              add    %rdx,%r12
>   32: e9 2a ff ff ff        jmp    0xffffff61
>   37: e8 20 d1 9a fd        call   0xfd9ad15c
>   3c: 48                    rex.W
>   3d: 81                    .byte 0x81
>   3e: eb 00                jmp    0x40

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ