[syzbot] [bpf?] KMSAN: uninit-value in __arg_track_join

31 views
Skip to first unread message

syzbot

unread,
May 29, 2026, 4:02:23 PMMay 29
to and...@kernel.org, a...@kernel.org, b...@vger.kernel.org, dan...@iogearbox.net, edd...@gmail.com, jo...@kernel.org, linux-...@vger.kernel.org, marti...@linux.dev, mem...@gmail.com, so...@kernel.org, syzkall...@googlegroups.com, yongho...@linux.dev
Hello,

syzbot found the following issue on:

HEAD commit: d60ec36cab33 Merge tag 'mm-hotfixes-stable-2026-05-25-16-2..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=116ac6ec580000
kernel config: https://syzkaller.appspot.com/x/.config?x=334b75e012487335
dashboard link: https://syzkaller.appspot.com/bug?extid=0098eed2cc898cdd672f
compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
userspace arch: i386

Unfortunately, I don't have any reproducer for this issue yet.

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/7e228ea8db12/disk-d60ec36c.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/83407360a5fe/vmlinux-d60ec36c.xz
kernel image: https://storage.googleapis.com/syzbot-assets/9e10d0866b6f/bzImage-d60ec36c.xz

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+0098ee...@syzkaller.appspotmail.com

=====================================================
BUG: KMSAN: uninit-value in __arg_track_join+0x15a/0x810 kernel/bpf/liveness.c:743
__arg_track_join+0x15a/0x810 kernel/bpf/liveness.c:743
arg_track_join+0x186/0x870 kernel/bpf/liveness.c:784
compute_subprog_args kernel/bpf/liveness.c:1623 [inline]
analyze_subprog+0x3eb0/0xff30 kernel/bpf/liveness.c:1799
bpf_compute_subprog_arg_access+0x3ee/0x2370 kernel/bpf/liveness.c:1937
bpf_compute_live_registers+0xfdb/0x3090 kernel/bpf/liveness.c:2149
bpf_check+0x5bcb/0x9360 kernel/bpf/verifier.c:20065
bpf_prog_load+0x28d2/0x2d00 kernel/bpf/syscall.c:3082
__sys_bpf+0x8e0/0xee0 kernel/bpf/syscall.c:6274
__do_sys_bpf kernel/bpf/syscall.c:6387 [inline]
__se_sys_bpf kernel/bpf/syscall.c:6385 [inline]
__ia32_sys_bpf+0xa4/0xf0 kernel/bpf/syscall.c:6385
ia32_sys_call+0x33e7/0x4360 arch/x86/include/generated/asm/syscalls_32.h:358
do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:83 [inline]
__do_fast_syscall_32+0x195/0x470 arch/x86/entry/syscall_32.c:307
do_fast_syscall_32+0x37/0x80 arch/x86/entry/syscall_32.c:332
do_SYSENTER_32+0x1f/0x30 arch/x86/entry/syscall_32.c:370
entry_SYSENTER_compat_after_hwframe+0x84/0x8e

Local variable old created at:
arg_track_join+0x86/0x870 kernel/bpf/liveness.c:783
compute_subprog_args kernel/bpf/liveness.c:1623 [inline]
analyze_subprog+0x3eb0/0xff30 kernel/bpf/liveness.c:1799

CPU: 0 UID: 0 PID: 18676 Comm: syz.0.5880 Tainted: G W syzkaller #0 PREEMPT(lazy)
Tainted: [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
=====================================================


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzk...@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup

Tejas MD

unread,
Jun 19, 2026, 9:54:14 AMJun 19
to syzkaller-bugs
#syz test: https://github.com/Tejas-MD/linux.git fix-branch

syzbot

unread,
Jun 19, 2026, 9:54:19 AMJun 19
to tejasmut...@gmail.com, syzkall...@googlegroups.com, tejasmut...@gmail.com, linux-...@vger.kernel.org
> #syz test: https://github.com/Tejas-MD/linux.git fix-branch

This crash does not have a reproducer. I cannot test it.
> --
> You received this message because you are subscribed to the Google Groups "syzkaller-bugs" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller-bug...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/syzkaller-bugs/6485c75e-8ce6-4de5-aed7-7ada081bbc1en%40googlegroups.com.

Yu, Tao1

unread,
3:48 AM (14 hours ago) 3:48 AM
to syzkaller-bugs

Please help review and verify the following fix.

KMSAN reports an uninit-value bug in __arg_track_join() when liveness analysis propagates arg_track states:

 

  BUG: KMSAN: uninit-value in __arg_track_join


arg_track carries its state in a union:

- precise pointers use .off[] with off_cnt > 0;

- offset-imprecise pointers use frame >= 0 with off_cnt == 0;

- fully-imprecise pointers use frame == ARG_IMPRECISE with .mask.

 

The bug is caused by ad-hoc state downgrades and partial state construction. Some paths only updated selected fields, such as switching to offset-imprecise by writing off_cnt = 0, or building temporary arg_track values by filling fields manually. Later joins then copied and merged these values and KMSAN could observe uninitialized union content when the state was interpreted through a different union member.

 

Fix this by normalizing arg_track state construction:

- introduce helpers for ARG_NONE, ARG_UNVISITED, offset-imprecise, and

  fully-imprecise states;

- use these helpers when producing downgraded states;

- replace in-place off_cnt-only downgrades with full object assignment;

- initialize temporary resolved states through the same constructors

  before filling precise offsets.

 

This keeps arg_track state transitions explicit and ensures every state seen by join/merge code is fully initialized.

 

Fixes: bf0c571f7feb ("bpf: introduce forward arg-tracking dataflow analysis")

Reported-by: syzbot+0098ee...@syzkaller.appspotmail.com

Signed-off-by: Yu Tao <tao...@intel.com>

---

 kernel/bpf/liveness.c | 65 ++++++++++++++++++++++++++++---------------

 1 file changed, 43 insertions(+), 22 deletions(-)

 

diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c index 0aadfbae0acc5..58b979c17c3e4 100644

--- a/kernel/bpf/liveness.c

+++ b/kernel/bpf/liveness.c

@@ -607,6 +607,31 @@ enum arg_track_state {

              ARG_IMPRECISE              = -3,       /* lost identity; .mask is arg bitmask */

 };

 

+static struct arg_track arg_track_state(s8 frame) {

+            return (struct arg_track){ .frame = frame }; }

+

+static struct arg_track arg_track_none(void) {

+            return arg_track_state(ARG_NONE);

+}

+

+static struct arg_track arg_track_unvisited(void) {

+            return arg_track_state(ARG_UNVISITED); }

+

+static struct arg_track arg_track_off_imprecise(s8 frame) {

+            return arg_track_state(frame);

+}

+

+static struct arg_track arg_track_imprecise(u16 mask) {

+            return (struct arg_track){ .mask = mask, .frame = ARG_IMPRECISE }; }

+

 /* Track callee stack slots fp-8 through fp-512 (64 slots of 8 bytes each) */  #define MAX_ARG_SPILL_SLOTS 64

 

@@ -693,8 +718,8 @@ static struct arg_track arg_single(s8 arg, s16 off)

  */

 static struct arg_track arg_merge_offsets(struct arg_track a, struct arg_track b)  {

-             struct arg_track result = { .frame = a.frame };

-             struct arg_track imp = { .frame = a.frame };

+            struct arg_track result = arg_track_state(a.frame);

+            struct arg_track imp = arg_track_off_imprecise(a.frame);

              int i = 0, j = 0, k = 0;

 

              while (i < a.off_cnt && j < b.off_cnt) { @@ -747,7 +772,7 @@ static struct arg_track arg_join_imprecise(struct arg_track a, struct arg_track

              else if (b.frame == ARG_IMPRECISE)

                             m |= b.mask;

 

-             return (struct arg_track){ .mask = m, .frame = ARG_IMPRECISE };

+            return arg_track_imprecise(m);

 }

 

 /* Join two arg_track values at merge points */ @@ -760,7 +785,7 @@ static struct arg_track __arg_track_join(struct arg_track a, struct arg_track b)

              if (a.frame == b.frame && a.frame >= 0) {

                             /* Both offset-imprecise: stay imprecise */

                             if (a.off_cnt == 0 || b.off_cnt == 0)

-                                          return (struct arg_track){ .frame = a.frame };

+                                         return arg_track_off_imprecise(a.frame);

                             /* Merge offset sets; falls back to off_cnt=0 if >4 */

                             return arg_merge_offsets(a, b);

              }

@@ -837,7 +862,7 @@ static void arg_track_alu64(struct arg_track *dst, const struct arg_track *src)

                              * rX += rY where rY is not arg derived

                              * rX += rX

                              */

-                           dst->off_cnt = 0;

+                           *dst = arg_track_off_imprecise(dst->frame);

                             return;

              }

              if (src->frame >= 0 && dst->frame == ARG_NONE) { @@ -845,8 +870,7 @@ static void arg_track_alu64(struct arg_track *dst, const struct arg_track *src)

                              * rX += rY where rX is not arg derived

                              * rY identity leaks into rX

                              */

-                           dst->off_cnt = 0;

-                           dst->frame = src->frame;

+                           *dst = arg_track_off_imprecise(src->frame);

                             return;

              }

 

@@ -875,7 +899,7 @@ static void arg_padd(struct arg_track *at, s64 delta)

                             s16 new_off;

 

                             if (arg_add(at->off[i], delta, &new_off)) {

-                                          at->off_cnt = 0;

+                                         *at = arg_track_off_imprecise(at->frame);

                                           return;

                             }

                             at->off[i] = new_off;

@@ -901,11 +925,8 @@ static struct arg_track fill_from_stack(struct bpf_insn *insn,

                                                                        struct arg_track *at_stack_out,

                                                                        int depth)

 {

-             struct arg_track imp = {

-                           .mask = (1u << (depth + 1)) - 1,

-                           .frame = ARG_IMPRECISE

-             };

-             struct arg_track result = { .frame = ARG_NONE };

+            struct arg_track imp = arg_track_imprecise((1u << (depth + 1)) - 1);

+            struct arg_track result = arg_track_state(ARG_NONE);

              int cnt, i;

 

              if (reg == BPF_REG_FP) {

@@ -940,7 +961,7 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out,

                                              int reg, struct arg_track *at_stack_out,

                                              struct arg_track *val, u32 sz)

 {

-             struct arg_track none = { .frame = ARG_NONE };

+            struct arg_track none = arg_track_none();

              struct arg_track new_val = sz == 8 ? *val : none;

              int cnt, i;

 

@@ -979,7 +1000,7 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out,

  */

 static void clear_overlapping_stack_slots(struct arg_track *at_stack, s16 off, u32 sz, int cnt)  {

-             struct arg_track none = { .frame = ARG_NONE };

+            struct arg_track none = arg_track_none();

 

              if (cnt == 0) {

                             for (int i = 0; i < MAX_ARG_SPILL_SLOTS; i++) @@ -1103,7 +1124,7 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn,

              u8 code = BPF_OP(insn->code);

              struct arg_track *dst = &at_out[insn->dst_reg];

              struct arg_track *src = &at_out[insn->src_reg];

-             struct arg_track none = { .frame = ARG_NONE };

+            struct arg_track none = arg_track_none();

              int r, slot;

 

              /* Handle stack arg stores and loads. */ @@ -1128,7 +1149,7 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn,

                                                          arg_padd(dst, -(s64)insn->imm);

                                           else

                                                          /* Any other 64-bit alu on the pointer makes it imprecise */

-                                                        dst->off_cnt = 0;

+                                                       *dst = arg_track_off_imprecise(dst->frame);

                             } /* else if dst->frame is imprecise it stays so */

              } else if (class == BPF_ALU64 && BPF_SRC(insn->code) == BPF_X) {

                             if (code == BPF_MOV) {

@@ -1379,11 +1400,11 @@ static int record_load_store_access(struct bpf_verifier_env *env,

 

              /* Resolve offsets: fold insn->off into arg_track */

              if (ptr->off_cnt > 0) {

+                           resolved = arg_track_state(ptr->frame);

                             resolved.off_cnt = ptr->off_cnt;

-                           resolved.frame = ptr->frame;

                             for (oi = 0; oi < ptr->off_cnt; oi++) {

                                           if (arg_add(ptr->off[oi], insn->off, &resolved.off[oi])) {

-                                                        resolved.off_cnt = 0;

+                                                       resolved = arg_track_off_imprecise(ptr->frame);

                                                          break;

                                           }

                             }

@@ -1630,8 +1651,8 @@ static int compute_subprog_args(struct bpf_verifier_env *env,

              struct arg_track (*at_stack_in)[MAX_ARG_SPILL_SLOTS] = NULL;

              struct arg_track *at_stack_out = NULL;

              struct arg_track at_stack_arg_entry[MAX_STACK_ARG_SLOTS];

-             struct arg_track unvisited = { .frame = ARG_UNVISITED };

-             struct arg_track none = { .frame = ARG_NONE };

+            struct arg_track unvisited = arg_track_unvisited();

+            struct arg_track none = arg_track_none();

              bool changed;

              int i, p, r, err = -ENOMEM;

 

@@ -1895,7 +1916,7 @@ static int analyze_subprog(struct bpf_verifier_env *env,

              for (int p = po_start; p < po_end; p++) {

                             int idx = env->cfg.insn_postorder[p];

                             struct arg_track callee_args[MAX_AT_TRACK_REGS] = {};

-                           struct arg_track none = { .frame = ARG_NONE };

+                           struct arg_track none = arg_track_none();

                             struct bpf_insn *insn = &insns[idx];

                             struct func_instance *callee_instance;

                             int callee, target;

--

2.34.1

Reply all
Reply to author
Forward
0 new messages