* [PATCH] bpf: normalize arg_track state construction
@ 2026-07-23 5:55 Tao Yu
2026-07-23 6:11 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Tao Yu @ 2026-07-23 5:55 UTC (permalink / raw)
To: bpf; +Cc: ast, daniel, andrii, eddyz87, tao1.yu,
syzbot+0098eed2cc898cdd672f
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+0098eed2cc898cdd672f@syzkaller.appspotmail.com
Signed-off-by: Yu Tao <tao1.yu@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
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] bpf: normalize arg_track state construction
2026-07-23 5:55 [PATCH] bpf: normalize arg_track state construction Tao Yu
@ 2026-07-23 6:11 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-23 6:11 UTC (permalink / raw)
To: Tao Yu; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The helper `arg_track_imprecise()` leaves union bytes uninitialized, failing to fully resolve the KMSAN uninit-value bug.
--
--- Patch [1]: [PATCH] bpf: normalize arg_track state construction ---
commit 66a3cf2cd9455714944be22e6ff4f7ab5a037e76
Author: Tao Yu <tao1.yu@intel.com>
bpf: normalize arg_track state construction
This commit introduces explicit helper functions to normalize arg_track
state construction and address a KMSAN uninitialized-value bug during
liveness analysis.
> 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
[ ... ]
> +static struct arg_track arg_track_imprecise(u16 mask)
> +{
> + return (struct arg_track){ .mask = mask, .frame = ARG_IMPRECISE };
> +}
[Severity: Medium]
Does this compound literal leave the remaining 6 bytes of the anonymous union
uninitialized?
Since the union contains both an 8-byte offset array and a 2-byte mask,
explicitly initializing only the mask member leaves the remaining bytes of
the union unspecified.
When this state is returned, such as from fill_from_stack(), and propagated
via arg_track_join() during liveness analysis, won't KMSAN continue to
trigger warnings when the structure is copied by value?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723055508.1403247-1-tao1.yu@intel.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-23 6:11 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 5:55 [PATCH] bpf: normalize arg_track state construction Tao Yu
2026-07-23 6:11 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox