* [PATCH stable 6.6.y v4 2/4] bpf: Remove mark_precise_scalar_ids()
From: Zhenzhong Wu @ 2026-06-21 17:27 UTC (permalink / raw)
To: bpf
Cc: netdev, linux-kernel, ast, daniel, john.fastabend, andrii,
martin.lau, song, yonghong.song, kpsingh, haoluo, jolsa,
menglong8.dong, eddyz87, shung-hsi.yu, stable, mykolal, tamird
In-Reply-To: <20260621172735.409355-1-jt26wzz@gmail.com>
From: Eduard Zingerman <eddyz87@gmail.com>
[ Upstream commit 842edb5507a1038e009d27e69d13b94b6f085763 ]
Function mark_precise_scalar_ids() is superseded by
bt_sync_linked_regs() and equal scalars tracking in jump history.
mark_precise_scalar_ids() propagates precision over registers sharing
same ID on parent/child state boundaries, while jump history records
allow bt_sync_linked_regs() to propagate same information with
instruction level granularity, which is strictly more precise.
This commit removes mark_precise_scalar_ids() and updates test cases
in progs/verifier_scalar_ids to reflect new verifier behavior.
The tests are updated in the following manner:
- mark_precise_scalar_ids() propagated precision regardless of
presence of conditional jumps, while new jump history based logic
only kicks in when conditional jumps are present.
Hence test cases are augmented with conditional jumps to still
trigger precision propagation.
- As equal scalars tracking no longer relies on parent/child state
boundaries some test cases are no longer interesting,
such test cases are removed, namely:
- precision_same_state and precision_cross_state are superseded by
linked_regs_bpf_k;
- precision_same_state_broken_link and equal_scalars_broken_link
are superseded by linked_regs_broken_link.
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20240718202357.1746514-3-eddyz87@gmail.com
[ zhenzhong: backport to 6.6.y after adapting the first linked-regs
history commit to the older scalar-id verifier layout. ]
Signed-off-by: Zhenzhong Wu <jt26wzz@gmail.com>
---
kernel/bpf/verifier.c | 115 ------------
.../selftests/bpf/progs/verifier_scalar_ids.c | 171 ++++++------------
.../testing/selftests/bpf/verifier/precise.c | 2 +-
3 files changed, 56 insertions(+), 232 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2268f095203e..f638b2d3a42f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4265,96 +4265,6 @@ static void mark_all_scalars_imprecise(struct bpf_verifier_env *env, struct bpf_
}
}
-static bool idset_contains(struct bpf_idset *s, u32 id)
-{
- u32 i;
-
- for (i = 0; i < s->count; ++i)
- if (s->ids[i] == id)
- return true;
-
- return false;
-}
-
-static int idset_push(struct bpf_idset *s, u32 id)
-{
- if (WARN_ON_ONCE(s->count >= ARRAY_SIZE(s->ids)))
- return -EFAULT;
- s->ids[s->count++] = id;
- return 0;
-}
-
-static void idset_reset(struct bpf_idset *s)
-{
- s->count = 0;
-}
-
-/* Collect a set of IDs for all registers currently marked as precise in env->bt.
- * Mark all registers with these IDs as precise.
- */
-static int mark_precise_scalar_ids(struct bpf_verifier_env *env, struct bpf_verifier_state *st)
-{
- struct bpf_idset *precise_ids = &env->idset_scratch;
- struct backtrack_state *bt = &env->bt;
- struct bpf_func_state *func;
- struct bpf_reg_state *reg;
- DECLARE_BITMAP(mask, 64);
- int i, fr;
-
- idset_reset(precise_ids);
-
- for (fr = bt->frame; fr >= 0; fr--) {
- func = st->frame[fr];
-
- bitmap_from_u64(mask, bt_frame_reg_mask(bt, fr));
- for_each_set_bit(i, mask, 32) {
- reg = &func->regs[i];
- if (!reg->id || reg->type != SCALAR_VALUE)
- continue;
- if (idset_push(precise_ids, reg->id))
- return -EFAULT;
- }
-
- bitmap_from_u64(mask, bt_frame_stack_mask(bt, fr));
- for_each_set_bit(i, mask, 64) {
- if (i >= func->allocated_stack / BPF_REG_SIZE)
- break;
- if (!is_spilled_scalar_reg(&func->stack[i]))
- continue;
- reg = &func->stack[i].spilled_ptr;
- if (!reg->id)
- continue;
- if (idset_push(precise_ids, reg->id))
- return -EFAULT;
- }
- }
-
- for (fr = 0; fr <= st->curframe; ++fr) {
- func = st->frame[fr];
-
- for (i = BPF_REG_0; i < BPF_REG_10; ++i) {
- reg = &func->regs[i];
- if (!reg->id)
- continue;
- if (!idset_contains(precise_ids, reg->id))
- continue;
- bt_set_frame_reg(bt, fr, i);
- }
- for (i = 0; i < func->allocated_stack / BPF_REG_SIZE; ++i) {
- if (!is_spilled_scalar_reg(&func->stack[i]))
- continue;
- reg = &func->stack[i].spilled_ptr;
- if (!reg->id)
- continue;
- if (!idset_contains(precise_ids, reg->id))
- continue;
- bt_set_frame_slot(bt, fr, i);
- }
- }
-
- return 0;
-}
-
/*
* __mark_chain_precision() backtracks BPF program instruction sequence and
* chain of verifier states making sure that register *regno* (if regno >= 0)
@@ -4487,31 +4397,6 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
bt->frame, last_idx, first_idx, subseq_idx);
}
- /* If some register with scalar ID is marked as precise,
- * make sure that all registers sharing this ID are also precise.
- * This is needed to estimate effect of sync_linked_regs().
- * Do this at the last instruction of each state,
- * bpf_reg_state::id fields are valid for these instructions.
- *
- * Allows to track precision in situation like below:
- *
- * r2 = unknown value
- * ...
- * --- state #0 ---
- * ...
- * r1 = r2 // r1 and r2 now share the same ID
- * ...
- * --- state #1 {r1.id = A, r2.id = A} ---
- * ...
- * if (r2 > 10) goto exit; // sync_linked_regs() assigns range to r1
- * ...
- * --- state #2 {r1.id = A, r2.id = A} ---
- * r3 = r10
- * r3 += r1 // need to mark both r1 and r2
- */
- if (mark_precise_scalar_ids(env, st))
- return -EFAULT;
-
if (last_idx < 0) {
/* we are at the entry into subprog, which
* is expected for global funcs, but only if
diff --git a/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c b/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c
index 22a6cf6e8255..f70392bf696c 100644
--- a/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c
+++ b/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c
@@ -5,54 +5,27 @@
#include "bpf_misc.h"
/* Check that precision marks propagate through scalar IDs.
- * Registers r{0,1,2} have the same scalar ID at the moment when r0 is
- * marked to be precise, this mark is immediately propagated to r{1,2}.
+ * Registers r{0,1,2} have the same scalar ID.
+ * Range information is propagated for scalars sharing same ID.
+ * Check that precision mark for r0 causes precision marks for r{1,2}
+ * when range information is propagated for 'if <reg> <op> <const>' insn.
*/
SEC("socket")
__success __log_level(2)
-__msg("frame0: regs=r0,r1,r2 stack= before 4: (bf) r3 = r10")
-__msg("frame0: regs=r0,r1,r2 stack= before 3: (bf) r2 = r0")
-__msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0")
-__msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255")
-__msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns")
-__flag(BPF_F_TEST_STATE_FREQ)
-__naked void precision_same_state(void)
-{
- asm volatile (
- /* r0 = random number up to 0xff */
- "call %[bpf_ktime_get_ns];"
- "r0 &= 0xff;"
- /* tie r0.id == r1.id == r2.id */
- "r1 = r0;"
- "r2 = r0;"
- /* force r0 to be precise, this immediately marks r1 and r2 as
- * precise as well because of shared IDs
- */
- "r3 = r10;"
- "r3 += r0;"
- "r0 = 0;"
- "exit;"
- :
- : __imm(bpf_ktime_get_ns)
- : __clobber_all);
-}
-
-/* Same as precision_same_state, but mark propagates through state /
- * parent state boundary.
- */
-SEC("socket")
-__success __log_level(2)
-__msg("frame0: last_idx 6 first_idx 5 subseq_idx -1")
-__msg("frame0: regs=r0,r1,r2 stack= before 5: (bf) r3 = r10")
+/* first 'if' branch */
+__msg("6: (0f) r3 += r0")
+__msg("frame0: regs=r0 stack= before 4: (25) if r1 > 0x7 goto pc+0")
__msg("frame0: parent state regs=r0,r1,r2 stack=:")
-__msg("frame0: regs=r0,r1,r2 stack= before 4: (05) goto pc+0")
__msg("frame0: regs=r0,r1,r2 stack= before 3: (bf) r2 = r0")
-__msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0")
-__msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255")
-__msg("frame0: parent state regs=r0 stack=:")
-__msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns")
+/* second 'if' branch */
+__msg("from 4 to 5: ")
+__msg("6: (0f) r3 += r0")
+__msg("frame0: regs=r0 stack= before 5: (bf) r3 = r10")
+__msg("frame0: regs=r0 stack= before 4: (25) if r1 > 0x7 goto pc+0")
+/* parent state already has r{0,1,2} as precise */
+__msg("frame0: parent state regs= stack=:")
__flag(BPF_F_TEST_STATE_FREQ)
-__naked void precision_cross_state(void)
+__naked void linked_regs_bpf_k(void)
{
asm volatile (
/* r0 = random number up to 0xff */
@@ -61,9 +34,8 @@ __naked void precision_cross_state(void)
/* tie r0.id == r1.id == r2.id */
"r1 = r0;"
"r2 = r0;"
- /* force checkpoint */
- "goto +0;"
- /* force r0 to be precise, this immediately marks r1 and r2 as
+ "if r1 > 7 goto +0;"
+ /* force r0 to be precise, this eventually marks r1 and r2 as
* precise as well because of shared IDs
*/
"r3 = r10;"
@@ -75,59 +47,18 @@ __naked void precision_cross_state(void)
: __clobber_all);
}
-/* Same as precision_same_state, but break one of the
+/* Same as linked_regs_bpf_k, but break one of the
* links, note that r1 is absent from regs=... in __msg below.
*/
SEC("socket")
__success __log_level(2)
-__msg("frame0: regs=r0,r2 stack= before 5: (bf) r3 = r10")
-__msg("frame0: regs=r0,r2 stack= before 4: (b7) r1 = 0")
-__msg("frame0: regs=r0,r2 stack= before 3: (bf) r2 = r0")
-__msg("frame0: regs=r0 stack= before 2: (bf) r1 = r0")
-__msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255")
-__msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns")
-__flag(BPF_F_TEST_STATE_FREQ)
-__naked void precision_same_state_broken_link(void)
-{
- asm volatile (
- /* r0 = random number up to 0xff */
- "call %[bpf_ktime_get_ns];"
- "r0 &= 0xff;"
- /* tie r0.id == r1.id == r2.id */
- "r1 = r0;"
- "r2 = r0;"
- /* break link for r1, this is the only line that differs
- * compared to the previous test
- */
- "r1 = 0;"
- /* force r0 to be precise, this immediately marks r1 and r2 as
- * precise as well because of shared IDs
- */
- "r3 = r10;"
- "r3 += r0;"
- "r0 = 0;"
- "exit;"
- :
- : __imm(bpf_ktime_get_ns)
- : __clobber_all);
-}
-
-/* Same as precision_same_state_broken_link, but with state /
- * parent state boundary.
- */
-SEC("socket")
-__success __log_level(2)
-__msg("frame0: regs=r0,r2 stack= before 6: (bf) r3 = r10")
-__msg("frame0: regs=r0,r2 stack= before 5: (b7) r1 = 0")
-__msg("frame0: parent state regs=r0,r2 stack=:")
-__msg("frame0: regs=r0,r1,r2 stack= before 4: (05) goto pc+0")
-__msg("frame0: regs=r0,r1,r2 stack= before 3: (bf) r2 = r0")
-__msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0")
-__msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255")
+__msg("7: (0f) r3 += r0")
+__msg("frame0: regs=r0 stack= before 6: (bf) r3 = r10")
__msg("frame0: parent state regs=r0 stack=:")
-__msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns")
+__msg("frame0: regs=r0 stack= before 5: (25) if r0 > 0x7 goto pc+0")
+__msg("frame0: parent state regs=r0,r2 stack=:")
__flag(BPF_F_TEST_STATE_FREQ)
-__naked void precision_cross_state_broken_link(void)
+__naked void linked_regs_broken_link(void)
{
asm volatile (
/* r0 = random number up to 0xff */
@@ -136,18 +67,13 @@ __naked void precision_cross_state_broken_link(void)
/* tie r0.id == r1.id == r2.id */
"r1 = r0;"
"r2 = r0;"
- /* force checkpoint, although link between r1 and r{0,2} is
- * broken by the next statement current precision tracking
- * algorithm can't react to it and propagates mark for r1 to
- * the parent state.
- */
- "goto +0;"
/* break link for r1, this is the only line that differs
- * compared to precision_cross_state()
+ * compared to the previous test
*/
"r1 = 0;"
- /* force r0 to be precise, this immediately marks r1 and r2 as
- * precise as well because of shared IDs
+ "if r0 > 7 goto +0;"
+ /* force r0 to be precise,
+ * this eventually marks r2 as precise because of shared IDs
*/
"r3 = r10;"
"r3 += r0;"
@@ -164,10 +90,16 @@ __naked void precision_cross_state_broken_link(void)
*/
SEC("socket")
__success __log_level(2)
-__msg("11: (0f) r2 += r1")
+__msg("12: (0f) r2 += r1")
/* Current state */
-__msg("frame2: last_idx 11 first_idx 10 subseq_idx -1")
-__msg("frame2: regs=r1 stack= before 10: (bf) r2 = r10")
+__msg("frame2: last_idx 12 first_idx 11 subseq_idx -1 ")
+__msg("frame2: regs=r1 stack= before 11: (bf) r2 = r10")
+__msg("frame2: parent state regs=r1 stack=")
+__msg("frame1: parent state regs= stack=")
+__msg("frame0: parent state regs= stack=")
+/* Parent state */
+__msg("frame2: last_idx 10 first_idx 10 subseq_idx 11 ")
+__msg("frame2: regs=r1 stack= before 10: (25) if r1 > 0x7 goto pc+0")
__msg("frame2: parent state regs=r1 stack=")
/* frame1.r{6,7} are marked because mark_precise_scalar_ids()
* looks for all registers with frame2.r1.id in the current state
@@ -192,7 +124,7 @@ __msg("frame1: regs=r1 stack= before 4: (85) call pc+1")
__msg("frame0: parent state regs=r1,r6 stack=")
/* Parent state */
__msg("frame0: last_idx 3 first_idx 1 subseq_idx 4")
-__msg("frame0: regs=r0,r1,r6 stack= before 3: (bf) r6 = r0")
+__msg("frame0: regs=r1,r6 stack= before 3: (bf) r6 = r0")
__msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0")
__msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255")
__flag(BPF_F_TEST_STATE_FREQ)
@@ -230,7 +162,8 @@ static __naked __noinline __used
void precision_many_frames__bar(void)
{
asm volatile (
- /* force r1 to be precise, this immediately marks:
+ "if r1 > 7 goto +0;"
+ /* force r1 to be precise, this eventually marks:
* - bar frame r1
* - foo frame r{1,6,7}
* - main frame r{1,6}
@@ -247,14 +180,16 @@ void precision_many_frames__bar(void)
*/
SEC("socket")
__success __log_level(2)
+__msg("11: (0f) r2 += r1")
/* foo frame */
-__msg("frame1: regs=r1 stack=-8,-16 before 9: (bf) r2 = r10")
+__msg("frame1: regs=r1 stack= before 10: (bf) r2 = r10")
+__msg("frame1: regs=r1 stack= before 9: (25) if r1 > 0x7 goto pc+0")
__msg("frame1: regs=r1 stack=-8,-16 before 8: (7b) *(u64 *)(r10 -16) = r1")
__msg("frame1: regs=r1 stack=-8 before 7: (7b) *(u64 *)(r10 -8) = r1")
__msg("frame1: regs=r1 stack= before 4: (85) call pc+2")
/* main frame */
-__msg("frame0: regs=r0,r1 stack=-8 before 3: (7b) *(u64 *)(r10 -8) = r1")
-__msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0")
+__msg("frame0: regs=r1 stack=-8 before 3: (7b) *(u64 *)(r10 -8) = r1")
+__msg("frame0: regs=r1 stack= before 2: (bf) r1 = r0")
__msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255")
__flag(BPF_F_TEST_STATE_FREQ)
__naked void precision_stack(void)
@@ -283,7 +218,8 @@ void precision_stack__foo(void)
*/
"*(u64*)(r10 - 8) = r1;"
"*(u64*)(r10 - 16) = r1;"
- /* force r1 to be precise, this immediately marks:
+ "if r1 > 7 goto +0;"
+ /* force r1 to be precise, this eventually marks:
* - foo frame r1,fp{-8,-16}
* - main frame r1,fp{-8}
*/
@@ -299,15 +235,17 @@ void precision_stack__foo(void)
SEC("socket")
__success __log_level(2)
/* r{6,7} */
-__msg("11: (0f) r3 += r7")
-__msg("frame0: regs=r6,r7 stack= before 10: (bf) r3 = r10")
+__msg("12: (0f) r3 += r7")
+__msg("frame0: regs=r7 stack= before 11: (bf) r3 = r10")
+__msg("frame0: regs=r7 stack= before 9: (25) if r7 > 0x7 goto pc+0")
/* ... skip some insns ... */
__msg("frame0: regs=r6,r7 stack= before 3: (bf) r7 = r0")
__msg("frame0: regs=r0,r6 stack= before 2: (bf) r6 = r0")
/* r{8,9} */
-__msg("12: (0f) r3 += r9")
-__msg("frame0: regs=r8,r9 stack= before 11: (0f) r3 += r7")
+__msg("13: (0f) r3 += r9")
+__msg("frame0: regs=r9 stack= before 12: (0f) r3 += r7")
/* ... skip some insns ... */
+__msg("frame0: regs=r9 stack= before 10: (25) if r9 > 0x7 goto pc+0")
__msg("frame0: regs=r8,r9 stack= before 7: (bf) r9 = r0")
__msg("frame0: regs=r0,r8 stack= before 6: (bf) r8 = r0")
__flag(BPF_F_TEST_STATE_FREQ)
@@ -328,8 +266,9 @@ __naked void precision_two_ids(void)
"r9 = r0;"
/* clear r0 id */
"r0 = 0;"
- /* force checkpoint */
- "goto +0;"
+ /* propagate equal scalars precision */
+ "if r7 > 7 goto +0;"
+ "if r9 > 7 goto +0;"
"r3 = r10;"
/* force r7 to be precise, this also marks r6 */
"r3 += r7;"
diff --git a/tools/testing/selftests/bpf/verifier/precise.c b/tools/testing/selftests/bpf/verifier/precise.c
index b0b1bcc668ad..59a020c35647 100644
--- a/tools/testing/selftests/bpf/verifier/precise.c
+++ b/tools/testing/selftests/bpf/verifier/precise.c
@@ -106,7 +106,7 @@
mark_precise: frame0: regs=r2 stack= before 22\
mark_precise: frame0: parent state regs=r2 stack=:\
mark_precise: frame0: last_idx 20 first_idx 20\
- mark_precise: frame0: regs=r2,r9 stack= before 20\
+ mark_precise: frame0: regs=r2 stack= before 20\
mark_precise: frame0: parent state regs=r2,r9 stack=:\
mark_precise: frame0: last_idx 19 first_idx 17\
mark_precise: frame0: regs=r2,r9 stack= before 19\
--
2.43.0
^ permalink raw reply related
* [PATCH stable 6.6.y v4 1/4] bpf: Track equal scalars history on per-instruction level
From: Zhenzhong Wu @ 2026-06-21 17:27 UTC (permalink / raw)
To: bpf
Cc: netdev, linux-kernel, ast, daniel, john.fastabend, andrii,
martin.lau, song, yonghong.song, kpsingh, haoluo, jolsa,
menglong8.dong, eddyz87, shung-hsi.yu, stable, mykolal, tamird,
Hao Sun
In-Reply-To: <20260621172735.409355-1-jt26wzz@gmail.com>
From: Eduard Zingerman <eddyz87@gmail.com>
[ Upstream commit 4bf79f9be434e000c8e12fe83b2f4402480f1460 ]
Use bpf_verifier_state->jmp_history to track which registers were
updated by find_equal_scalars() (renamed to collect_linked_regs())
when conditional jump was verified. Use recorded information in
backtrack_insn() to propagate precision.
E.g. for the following program:
while verifying instructions
1: r1 = r0 |
2: if r1 < 8 goto ... | push r0,r1 as linked registers in jmp_history
3: if r0 > 16 goto ... | push r0,r1 as linked registers in jmp_history
4: r2 = r10 |
5: r2 += r0 v mark_chain_precision(r0)
while doing mark_chain_precision(r0)
5: r2 += r0 | mark r0 precise
4: r2 = r10 |
3: if r0 > 16 goto ... | mark r0,r1 as precise
2: if r1 < 8 goto ... | mark r0,r1 as precise
1: r1 = r0 v
Technically, do this as follows:
- Use 10 bits to identify each register that gains range because of
sync_linked_regs():
- 3 bits for frame number;
- 6 bits for register or stack slot number;
- 1 bit to indicate if register is spilled.
- Use u64 as a vector of 6 such records + 4 bits for vector length.
- Augment struct bpf_jmp_history_entry with a field 'linked_regs'
representing such vector.
- When doing check_cond_jmp_op() remember up to 6 registers that
gain range because of sync_linked_regs() in such a vector.
- Don't propagate range information and reset IDs for registers that
don't fit in 6-value vector.
- Push a pair {instruction index, linked registers vector}
to bpf_verifier_state->jmp_history.
- When doing backtrack_insn() check if any of recorded linked
registers is currently marked precise, if so mark all linked
registers as precise.
This also requires fixes for two test_verifier tests:
- precise: test 1
- precise: test 2
Both tests contain the following instruction sequence:
19: (bf) r2 = r9 ; R2=scalar(id=3) R9=scalar(id=3)
20: (a5) if r2 < 0x8 goto pc+1 ; R2=scalar(id=3,umin=8)
21: (95) exit
22: (07) r2 += 1 ; R2_w=scalar(id=3+1,...)
23: (bf) r1 = r10 ; R1_w=fp0 R10=fp0
24: (07) r1 += -8 ; R1_w=fp-8
25: (b7) r3 = 0 ; R3_w=0
26: (85) call bpf_probe_read_kernel#113
The call to bpf_probe_read_kernel() at (26) forces r2 to be precise.
Previously, this forced all registers with same id to become precise
immediately when mark_chain_precision() is called.
After this change, the precision is propagated to registers sharing
same id only when 'if' instruction is backtracked.
Hence verification log for both tests is changed:
regs=r2,r9 -> regs=r2 for instructions 25..20.
Fixes: 904e6ddf4133 ("bpf: Use scalar ids in mark_chain_precision()")
Reported-by: Hao Sun <sunhao.th@gmail.com>
Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20240718202357.1746514-2-eddyz87@gmail.com
Closes: https://lore.kernel.org/bpf/CAEf4BzZ0xidVCqB47XnkXcNhkPWF6_nTV7yt+_Lf0kcFEut2Mg@mail.gmail.com/
[ zhenzhong: backport to 6.6.y verifier layout and adapt
sync_linked_regs() to the pre-BPF_ADD_CONST scalar-id code. ]
Signed-off-by: Zhenzhong Wu <jt26wzz@gmail.com>
---
include/linux/bpf_verifier.h | 4 +
kernel/bpf/verifier.c | 253 ++++++++++++++++--
.../bpf/progs/verifier_subprog_precision.c | 2 +-
.../testing/selftests/bpf/verifier/precise.c | 2 +-
4 files changed, 237 insertions(+), 24 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index dba211d3bb9a..9a3b93c24f19 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -345,6 +345,10 @@ struct bpf_jmp_history_entry {
u32 prev_idx : 22;
/* special flags, e.g., whether insn is doing register stack spill/load */
u32 flags : 10;
+ /* additional registers that need precision tracking when this
+ * jump is backtracked, vector of six 10-bit records
+ */
+ u64 linked_regs;
};
/* Maximum number of register states that can exist at once */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0d90236d0ad9..2268f095203e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3461,9 +3461,87 @@ static bool is_jmp_point(struct bpf_verifier_env *env, int insn_idx)
return env->insn_aux_data[insn_idx].jmp_point;
}
+#define LR_FRAMENO_BITS 3
+#define LR_SPI_BITS 6
+#define LR_ENTRY_BITS (LR_SPI_BITS + LR_FRAMENO_BITS + 1)
+#define LR_SIZE_BITS 4
+#define LR_FRAMENO_MASK ((1ull << LR_FRAMENO_BITS) - 1)
+#define LR_SPI_MASK ((1ull << LR_SPI_BITS) - 1)
+#define LR_SIZE_MASK ((1ull << LR_SIZE_BITS) - 1)
+#define LR_SPI_OFF LR_FRAMENO_BITS
+#define LR_IS_REG_OFF (LR_SPI_BITS + LR_FRAMENO_BITS)
+#define LINKED_REGS_MAX 6
+
+struct linked_reg {
+ u8 frameno;
+ union {
+ u8 spi;
+ u8 regno;
+ };
+ bool is_reg;
+};
+
+struct linked_regs {
+ int cnt;
+ struct linked_reg entries[LINKED_REGS_MAX];
+};
+
+static struct linked_reg *linked_regs_push(struct linked_regs *s)
+{
+ if (s->cnt < LINKED_REGS_MAX)
+ return &s->entries[s->cnt++];
+
+ return NULL;
+}
+
+/* Use u64 as a vector of 6 10-bit values, use first 4-bits to track
+ * number of elements currently in stack.
+ * Pack one history entry for linked registers as 10 bits in the following format:
+ * - 3-bits frameno
+ * - 6-bits spi_or_reg
+ * - 1-bit is_reg
+ */
+static u64 linked_regs_pack(struct linked_regs *s)
+{
+ u64 val = 0;
+ int i;
+
+ for (i = 0; i < s->cnt; ++i) {
+ struct linked_reg *e = &s->entries[i];
+ u64 tmp = 0;
+
+ tmp |= e->frameno;
+ tmp |= e->spi << LR_SPI_OFF;
+ tmp |= (e->is_reg ? 1 : 0) << LR_IS_REG_OFF;
+
+ val <<= LR_ENTRY_BITS;
+ val |= tmp;
+ }
+ val <<= LR_SIZE_BITS;
+ val |= s->cnt;
+ return val;
+}
+
+static void linked_regs_unpack(u64 val, struct linked_regs *s)
+{
+ int i;
+
+ s->cnt = val & LR_SIZE_MASK;
+ val >>= LR_SIZE_BITS;
+
+ for (i = 0; i < s->cnt; ++i) {
+ struct linked_reg *e = &s->entries[i];
+
+ e->frameno = val & LR_FRAMENO_MASK;
+ e->spi = (val >> LR_SPI_OFF) & LR_SPI_MASK;
+ e->is_reg = (val >> LR_IS_REG_OFF) & 0x1;
+ val >>= LR_ENTRY_BITS;
+ }
+}
+
/* for any branch, call, exit record the history of jmps in the given state */
static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur,
- int insn_flags)
+ int insn_flags, u64 linked_regs)
{
u32 cnt = cur->jmp_history_cnt;
struct bpf_jmp_history_entry *p;
@@ -3479,6 +3557,10 @@ static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_st
"verifier insn history bug: insn_idx %d cur flags %x new flags %x\n",
env->insn_idx, env->cur_hist_ent->flags, insn_flags);
env->cur_hist_ent->flags |= insn_flags;
+ WARN_ONCE(env->cur_hist_ent->linked_regs != 0,
+ "verifier insn history bug: insn_idx %d linked_regs != 0: %#llx\n",
+ env->insn_idx, env->cur_hist_ent->linked_regs);
+ env->cur_hist_ent->linked_regs = linked_regs;
return 0;
}
@@ -3493,6 +3575,7 @@ static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_st
p->idx = env->insn_idx;
p->prev_idx = env->prev_insn_idx;
p->flags = insn_flags;
+ p->linked_regs = linked_regs;
cur->jmp_history_cnt = cnt;
env->cur_hist_ent = p;
@@ -3668,6 +3751,11 @@ static inline bool bt_is_reg_set(struct backtrack_state *bt, u32 reg)
return bt->reg_masks[bt->frame] & (1 << reg);
}
+static inline bool bt_is_frame_reg_set(struct backtrack_state *bt, u32 frame, u32 reg)
+{
+ return bt->reg_masks[frame] & (1 << reg);
+}
+
static inline bool bt_is_frame_slot_set(struct backtrack_state *bt, u32 frame, u32 slot)
{
return bt->stack_masks[frame] & (1ull << slot);
@@ -3717,6 +3805,42 @@ static void fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask)
}
}
+/* If any register R in hist->linked_regs is marked as precise in bt,
+ * do bt_set_frame_{reg,slot}(bt, R) for all registers in hist->linked_regs.
+ */
+static void bt_sync_linked_regs(struct backtrack_state *bt, struct bpf_jmp_history_entry *hist)
+{
+ struct linked_regs linked_regs;
+ bool some_precise = false;
+ int i;
+
+ if (!hist || hist->linked_regs == 0)
+ return;
+
+ linked_regs_unpack(hist->linked_regs, &linked_regs);
+ for (i = 0; i < linked_regs.cnt; ++i) {
+ struct linked_reg *e = &linked_regs.entries[i];
+
+ if ((e->is_reg && bt_is_frame_reg_set(bt, e->frameno, e->regno)) ||
+ (!e->is_reg && bt_is_frame_slot_set(bt, e->frameno, e->spi))) {
+ some_precise = true;
+ break;
+ }
+ }
+
+ if (!some_precise)
+ return;
+
+ for (i = 0; i < linked_regs.cnt; ++i) {
+ struct linked_reg *e = &linked_regs.entries[i];
+
+ if (e->is_reg)
+ bt_set_frame_reg(bt, e->frameno, e->regno);
+ else
+ bt_set_frame_slot(bt, e->frameno, e->spi);
+ }
+}
+
static bool calls_callback(struct bpf_verifier_env *env, int insn_idx);
/* For given verifier state backtrack_insn() is called from the last insn to
@@ -3756,6 +3880,12 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
print_bpf_insn(&cbs, insn, env->allow_ptr_leaks);
}
+ /* If there is a history record that some registers gained range at this insn,
+ * propagate precision marks to those registers, so that bt_is_reg_set()
+ * accounts for these registers.
+ */
+ bt_sync_linked_regs(bt, hist);
+
if (class == BPF_ALU || class == BPF_ALU64) {
if (!bt_is_reg_set(bt, dreg))
return 0;
@@ -3985,7 +4115,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
*/
bt_set_reg(bt, dreg);
bt_set_reg(bt, sreg);
- /* else dreg <cond> K
+ } else if (BPF_SRC(insn->code) == BPF_K) {
+ /* dreg <cond> K
* Only dreg still needs precision before
* this insn, so for the K-based conditional
* there is nothing new to be marked.
@@ -4003,6 +4134,10 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
/* to be analyzed */
return -ENOTSUPP;
}
+ /* Propagate precision marks to linked registers, to account for
+ * registers marked as precise in this function.
+ */
+ bt_sync_linked_regs(bt, hist);
return 0;
}
@@ -4354,7 +4489,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
/* If some register with scalar ID is marked as precise,
* make sure that all registers sharing this ID are also precise.
- * This is needed to estimate effect of find_equal_scalars().
+ * This is needed to estimate effect of sync_linked_regs().
* Do this at the last instruction of each state,
* bpf_reg_state::id fields are valid for these instructions.
*
@@ -4368,7 +4503,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
* ...
* --- state #1 {r1.id = A, r2.id = A} ---
* ...
- * if (r2 > 10) goto exit; // find_equal_scalars() assigns range to r1
+ * if (r2 > 10) goto exit; // sync_linked_regs() assigns range to r1
* ...
* --- state #2 {r1.id = A, r2.id = A} ---
* r3 = r10
@@ -4736,7 +4871,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
}
if (insn_flags)
- return push_jmp_history(env, env->cur_state, insn_flags);
+ return push_jmp_history(env, env->cur_state, insn_flags, 0);
return 0;
}
@@ -5032,7 +5167,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
insn_flags = 0; /* we are not restoring spilled register */
}
if (insn_flags)
- return push_jmp_history(env, env->cur_state, insn_flags);
+ return push_jmp_history(env, env->cur_state, insn_flags, 0);
return 0;
}
@@ -13540,7 +13675,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
ptr_reg = dst_reg;
else
/* Make sure ID is cleared otherwise dst_reg min/max could be
- * incorrectly propagated into other registers by find_equal_scalars()
+ * incorrectly propagated into other registers by sync_linked_regs()
*/
dst_reg->id = 0;
if (BPF_SRC(insn->code) == BPF_X) {
@@ -13700,7 +13835,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
*/
if (need_id)
/* Assign src and dst registers the same ID
- * that will be used by find_equal_scalars()
+ * that will be used by sync_linked_regs()
* to propagate min/max range.
*/
src_reg->id = ++env->id_gen;
@@ -13746,7 +13881,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
copy_register_state(dst_reg, src_reg);
/* Make sure ID is cleared if src_reg is not in u32
* range otherwise dst_reg min/max could be incorrectly
- * propagated into src_reg by find_equal_scalars()
+ * propagated into src_reg by sync_linked_regs()
*/
if (!is_src_reg_u32)
dst_reg->id = 0;
@@ -14564,19 +14699,75 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
return true;
}
-static void find_equal_scalars(struct bpf_verifier_state *vstate,
- struct bpf_reg_state *known_reg)
+static void __collect_linked_regs(struct linked_regs *reg_set, struct bpf_reg_state *reg,
+ u32 id, u32 frameno, u32 spi_or_reg, bool is_reg)
{
- struct bpf_func_state *state;
+ struct linked_reg *e;
+
+ if (reg->type != SCALAR_VALUE || reg->id != id)
+ return;
+
+ e = linked_regs_push(reg_set);
+ if (e) {
+ e->frameno = frameno;
+ e->is_reg = is_reg;
+ e->regno = spi_or_reg;
+ } else {
+ reg->id = 0;
+ }
+}
+
+/* For all R being scalar registers or spilled scalar registers
+ * in verifier state, save R in linked_regs if R->id == id.
+ * If there are too many Rs sharing same id, reset id for leftover Rs.
+ */
+static void collect_linked_regs(struct bpf_verifier_state *vstate, u32 id,
+ struct linked_regs *linked_regs)
+{
+ struct bpf_func_state *func;
struct bpf_reg_state *reg;
+ int i, j;
- bpf_for_each_reg_in_vstate(vstate, state, reg, ({
- if (reg->type == SCALAR_VALUE && reg->id == known_reg->id) {
+ for (i = vstate->curframe; i >= 0; i--) {
+ func = vstate->frame[i];
+ for (j = 0; j < BPF_REG_FP; j++) {
+ reg = &func->regs[j];
+ __collect_linked_regs(linked_regs, reg, id, i, j, true);
+ }
+ for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) {
+ if (!is_spilled_reg(&func->stack[j]))
+ continue;
+ reg = &func->stack[j].spilled_ptr;
+ __collect_linked_regs(linked_regs, reg, id, i, j, false);
+ }
+ }
+}
+
+/* For all R in linked_regs, copy known_reg range into R
+ * if R->id == known_reg->id.
+ */
+static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_state *known_reg,
+ struct linked_regs *linked_regs)
+{
+ struct bpf_reg_state *reg;
+ struct linked_reg *e;
+ int i;
+
+ for (i = 0; i < linked_regs->cnt; ++i) {
+ e = &linked_regs->entries[i];
+ reg = e->is_reg ? &vstate->frame[e->frameno]->regs[e->regno]
+ : &vstate->frame[e->frameno]->stack[e->spi].spilled_ptr;
+ if (reg->type != SCALAR_VALUE || reg == known_reg)
+ continue;
+ if (reg->id != known_reg->id)
+ continue;
+ {
s32 saved_subreg_def = reg->subreg_def;
+
copy_register_state(reg, known_reg);
reg->subreg_def = saved_subreg_def;
}
- }));
+ }
}
static int check_cond_jmp_op(struct bpf_verifier_env *env,
@@ -14587,6 +14778,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
struct bpf_reg_state *regs = this_branch->frame[this_branch->curframe]->regs;
struct bpf_reg_state *dst_reg, *other_branch_regs, *src_reg = NULL;
struct bpf_reg_state *eq_branch_regs;
+ struct linked_regs linked_regs = {};
u8 opcode = BPF_OP(insn->code);
bool is_jmp32;
int pred = -1;
@@ -14704,6 +14896,21 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
return 0;
}
+ /* Push scalar registers sharing same ID to jump history,
+ * do this before creating 'other_branch', so that both
+ * 'this_branch' and 'other_branch' share this history
+ * if parent state is created.
+ */
+ if (BPF_SRC(insn->code) == BPF_X && src_reg->type == SCALAR_VALUE && src_reg->id)
+ collect_linked_regs(this_branch, src_reg->id, &linked_regs);
+ if (dst_reg->type == SCALAR_VALUE && dst_reg->id)
+ collect_linked_regs(this_branch, dst_reg->id, &linked_regs);
+ if (linked_regs.cnt > 1) {
+ err = push_jmp_history(env, this_branch, 0, linked_regs_pack(&linked_regs));
+ if (err)
+ return err;
+ }
+
other_branch = push_stack(env, *insn_idx + insn->off + 1, *insn_idx,
false);
if (!other_branch)
@@ -14746,8 +14953,9 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
src_reg, dst_reg, opcode);
if (src_reg->id &&
!WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
- find_equal_scalars(this_branch, src_reg);
- find_equal_scalars(other_branch, &other_branch_regs[insn->src_reg]);
+ sync_linked_regs(this_branch, src_reg, &linked_regs);
+ sync_linked_regs(other_branch, &other_branch_regs[insn->src_reg],
+ &linked_regs);
}
}
@@ -14759,8 +14967,9 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
if (dst_reg->type == SCALAR_VALUE && dst_reg->id &&
!WARN_ON_ONCE(dst_reg->id != other_branch_regs[insn->dst_reg].id)) {
- find_equal_scalars(this_branch, dst_reg);
- find_equal_scalars(other_branch, &other_branch_regs[insn->dst_reg]);
+ sync_linked_regs(this_branch, dst_reg, &linked_regs);
+ sync_linked_regs(other_branch, &other_branch_regs[insn->dst_reg],
+ &linked_regs);
}
/* if one pointer register is compared to another pointer
@@ -16182,7 +16391,7 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
*
* First verification path is [1-6]:
* - at (4) same bpf_reg_state::id (b) would be assigned to r6 and r7;
- * - at (5) r6 would be marked <= X, find_equal_scalars() would also mark
+ * - at (5) r6 would be marked <= X, sync_linked_regs() would also mark
* r7 <= X, because r6 and r7 share same id.
* Next verification path is [1-4, 6].
*
@@ -16915,7 +17124,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
* the current state.
*/
if (is_jmp_point(env, env->insn_idx))
- err = err ? : push_jmp_history(env, cur, 0);
+ err = err ? : push_jmp_history(env, cur, 0, 0);
err = err ? : propagate_precision(env, &sl->state);
if (err)
return err;
@@ -17181,7 +17390,7 @@ static int do_check(struct bpf_verifier_env *env)
}
if (is_jmp_point(env, env->insn_idx)) {
- err = push_jmp_history(env, state, 0);
+ err = push_jmp_history(env, state, 0, 0);
if (err)
return err;
}
diff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
index 4b8b0f45d17d..a188e26f04da 100644
--- a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
+++ b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
@@ -141,7 +141,7 @@ __msg("mark_precise: frame0: last_idx 14 first_idx 9")
__msg("mark_precise: frame0: regs=r6 stack= before 13: (bf) r1 = r7")
__msg("mark_precise: frame0: regs=r6 stack= before 12: (27) r6 *= 4")
__msg("mark_precise: frame0: regs=r6 stack= before 11: (25) if r6 > 0x3 goto pc+4")
-__msg("mark_precise: frame0: regs=r6 stack= before 10: (bf) r6 = r0")
+__msg("mark_precise: frame0: regs=r0,r6 stack= before 10: (bf) r6 = r0")
__msg("mark_precise: frame0: regs=r0 stack= before 9: (85) call bpf_loop")
/* State entering callback body popped from states stack */
__msg("from 9 to 17: frame1:")
diff --git a/tools/testing/selftests/bpf/verifier/precise.c b/tools/testing/selftests/bpf/verifier/precise.c
index 8a2ff81d8350..b0b1bcc668ad 100644
--- a/tools/testing/selftests/bpf/verifier/precise.c
+++ b/tools/testing/selftests/bpf/verifier/precise.c
@@ -44,7 +44,7 @@
mark_precise: frame0: regs=r2 stack= before 23\
mark_precise: frame0: regs=r2 stack= before 22\
mark_precise: frame0: regs=r2 stack= before 20\
- mark_precise: frame0: parent state regs=r2 stack=:\
+ mark_precise: frame0: parent state regs=r2,r9 stack=:\
mark_precise: frame0: last_idx 19 first_idx 10\
mark_precise: frame0: regs=r2,r9 stack= before 19\
mark_precise: frame0: regs=r9 stack= before 18\
--
2.43.0
^ permalink raw reply related
* [PATCH stable 6.6.y v4 0/4] bpf: linked scalar precision fixes
From: Zhenzhong Wu @ 2026-06-21 17:27 UTC (permalink / raw)
To: bpf
Cc: netdev, linux-kernel, ast, daniel, john.fastabend, andrii,
martin.lau, song, yonghong.song, kpsingh, haoluo, jolsa,
menglong8.dong, eddyz87, shung-hsi.yu, stable, mykolal, tamird
Hi,
This v4 targets 6.6.y and keeps the v3 backport strategy: use the full
upstream linked-scalar precision-tracking series, instead of the earlier
d028f87517d6/9e314f5d8682 not-equal refinement backport path.
The original observed failure was found in Rust/Aya-generated eBPF
around helper calls. Rust match lowering can keep a helper return value
and a scalar filled through a by-reference helper argument in the same
enum-style control flow. That makes it easy for the verifier-visible
scalar values to become linked by scalar id.
The relevant verifier-log bytecode from the original reproducer is
below. The later instructions only store r7 into a map so user space can
observe which branch the verifier kept.
15: (85) call bpf_get_func_ret#184 ; R0_w=scalar() fp-8_w=mmmmmmmm
16: (79) r7 = *(u64 *)(r10 -8) ; R7_w=scalar() R10=fp0
17: (15) if r0 == 0x0 goto pc+1 ; R0_w=scalar()
18: (bf) r7 = r0 ; R0=scalar(id=1) R7=scalar(id=1)
19: (55) if r0 != 0x0 goto pc+6 ; R0=0
20: (67) r7 <<= 32 ; R7_w=0
21: (77) r7 >>= 32 ; R7_w=0
22: (b7) r1 = 1 ; R1_w=1
23: (55) if r7 != 0xf goto pc+1
The important verifier state shape is:
1. The program checks "if r0 == 0". The jump target is the success
path, and the fallthrough path is the failure path.
2. On the failure path, "r7 = r0" gives r0 and r7 the same scalar id.
The real success path skips that assignment, so r7 is independent
there.
3. At the later "if r0 != 0" check, an affected verifier can explore
an impossible continuation where r0 is zero and r7 is narrowed
through the shared scalar id as well.
4. That impossible continuation reaches the return-value comparison
with the wrong r7 value. When the real success path is analyzed
later, state pruning can consider it safe against the earlier
cached verifier state and skip the real continuation.
The root cause is verifier scalar state tracking, not helper-specific
behavior. A helper return value in r0 and another scalar can become
linked by scalar id on one branch. The real success path can skip that
assignment, so the two verifier states are not equivalent.
The relevant pruning point is that regsafe()/states_equal() accepted
the real success-path state against an earlier cached state where r0 was
an imprecise scalar and r7 constraints were loose enough to cover the
current r7. In the impossible path, r0 and r7 are linked by scalar id
after instruction 18. In the real success path, instruction 18 is
skipped and that scalar-id relationship does not exist. These states
should therefore not be treated as equivalent for pruning.
The upstream linked-scalar precision series fixes that root cause by
recording, in jmp_history, which linked registers were synchronized at
each conditional jump and by using that per-instruction history during
precision backtracking. This covers both the original r0 == 0 /
r0 != 0 shape and the r0 == 1 / r0 != 1 shape used by the separate
runtime selftest.
A Rust/Aya-specific runtime reproducer/selftest discussed in the v2
thread has been submitted separately to bpf-next for review:
https://lore.kernel.org/bpf/20260611160749.391279-1-jt26wzz@gmail.com/
That reproducer keeps the same helper-return/control-flow shape but
shifts the success value to 1 before branching. This avoids depending
on the not-equal-zero refinement and exercises linked scalar precision
during state pruning directly. It uses bpf_skb_load_bytes() in the
normal tc test-run path and does not require fexit attach or
bpf_get_func_ret(). It is not included in this stable series because
per review feedback it should go through bpf-next first before being
considered for stable.
Targeted results for that separate helper-status runtime reproducer are:
v6.6.142 + reproducer: FAIL
v6.6.142 + v2 d028/9e backport path + reproducer: FAIL
v6.6.142 + this linked-scalars series + reproducer: PASS
bpf-next + reproducer: PASS
Changes since v3:
- add the tools/testing/selftests/bpf/verifier/precise.c expected-log
update for "precise: test 1";
- drop the v3-only collect_linked_regs() singleton cleanup and use
the final upstream linked_regs.cnt > 1 history-recording check in
patch 1.
v3:
https://lore.kernel.org/r/cover.1781194510.git.jt26wzz@gmail.com/
v2:
https://lore.kernel.org/r/20260607170959.823755-1-jt26wzz@gmail.com/
RFC v1:
https://lore.kernel.org/r/20260601180400.1381736-1-jt26wzz@gmail.com/
Backport details:
This series is based on v6.6.142 / stable/linux-6.6.y commit
924b4a879cbb ("Linux 6.6.142"). I would like it applied to 6.6.y first.
The same issue is reproducible on 6.1.y, 5.15.y, and 5.10.y, but those
trees need separate older-layout adaptations.
Instead of backporting the d028f87517d6 not-equal refinement plus the
9e314f5d8682 range-combining prerequisite, this series backports the
full upstream linked-scalar precision-tracking series:
4bf79f9be434
bpf: Track equal scalars history on per-instruction level
842edb5507a1
bpf: Remove mark_precise_scalar_ids()
bebc17b1c03b
selftests/bpf: Tests for per-insn sync_linked_regs() precision
tracking
cfbf25481d6d
selftests/bpf: Update comments find_equal_scalars->sync_linked_regs
Upstream series:
https://lore.kernel.org/r/20240718202357.1746514-1-eddyz87@gmail.com/
Patches 1 and 2 are the verifier changes from that upstream series. The
main 6.6.y-specific verifier adaptation is in patch 1: 6.6.y does not
yet have the newer BPF_ADD_CONST scalar-id representation, so
sync_linked_regs() is adapted to the older scalar-id layout. Patch 1
otherwise keeps the upstream linked_regs.cnt > 1 history-recording
condition.
Patch 1 also carries the matching upstream test_verifier expected-log
change for tools/testing/selftests/bpf/verifier/precise.c that v3
missed. On 6.6.y this is a one-line update to the "precise: test 1"
parent-state log, from regs=r2 to regs=r2,r9. Patch 2 then follows on
top of the adapted layout.
Patches 3 and 4 bring the upstream verifier selftests and comment
updates. Patch 3 has one 6.6.y-specific log adaptation:
linked_regs_broken_link_2 keeps the "div by zero" reject check, but
drops the upstream mark_precise log expectations because 6.6.y does not
derive the scalar-vs-scalar range for that non-constant JMP_X
comparison. Patch 4 only updates the two pre-existing comments that are
present in 6.6.y.
Relevant selftest results on 6.6.y with this v4 backport:
test_verifier:
788 PASSED, 0 SKIPPED, 0 FAILED
test_progs -t verifier_scalar_ids:
all 18 verifier_scalar_ids subtests passed
Thanks to Shung-Hsi Yu for reviewing v2/v3 and suggesting the upstream
linked-scalar precision series as the preferred backport direction.
Eduard Zingerman (4):
bpf: Track equal scalars history on per-instruction level
bpf: Remove mark_precise_scalar_ids()
selftests/bpf: Tests for per-insn sync_linked_regs() precision
tracking
selftests/bpf: Update comments find_equal_scalars->sync_linked_regs
include/linux/bpf_verifier.h | 4 +
kernel/bpf/verifier.c | 364 +++++++++++-------
.../selftests/bpf/progs/verifier_scalar_ids.c | 253 ++++++++----
.../selftests/bpf/progs/verifier_spill_fill.c | 4 +-
.../bpf/progs/verifier_subprog_precision.c | 2 +-
.../testing/selftests/bpf/verifier/precise.c | 4 +-
6 files changed, 415 insertions(+), 216 deletions(-)
base-commit: 924b4a879cbb75aef37c160b955b92f6894b11a4
--
2.43.0
^ permalink raw reply
* Re: [PATCH net 0/2] nfc: llcp: fix OOB reads and integer bugs in TLV parsers
From: David Heidelberg @ 2026-06-21 16:52 UTC (permalink / raw)
To: Muhammad Bilal, netdev
Cc: linux-kernel, oe-linux-nfc, david+nfc, davem, edumazet, kuba,
pabeni, horms, stable
In-Reply-To: <20260519011937.12903-1-meatuni001@gmail.com>
On 19/05/2026 03:19, Muhammad Bilal wrote:
> This series fixes memory safety bugs in the NFC LLCP TLV parsing code,
> reachable from a remote NFC peer via crafted LLCP frames.
>
> Patch 1 fixes nfc_llcp_parse_gb_tlv() and nfc_llcp_parse_connection_tlv():
> - u8 offset wraps to zero after 255 (widened to u16)
> - OOB read of TLV header on truncated buffer
> - OOB read of value field via attacker-controlled length byte
>
> Patch 2 fixes nfc_llcp_recv_snl():
> - OOB read of TLV header when tlv_len - offset == 1
> - OOB read of SDREQ value via attacker-controlled length
> - SIZE_MAX underflow when length == 0 in service_name_len,
> bypassing the sn_len == 0 guard in nfc_llcp_sock_from_sn()
>
> Previously reported to security@kernel.org on 2026-05-15. Willy Tarreau
> advised posting to public lists as NFC is currently orphaned.
>
> Muhammad Bilal (2):
> nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers
> nfc: llcp: add missing bounds checks in nfc_llcp_recv_snl()
>
> net/nfc/llcp_commands.c | 28 ++++++++++++++++++++++++++--
> net/nfc/llcp_core.c | 23 +++++++++++++++++++++--
> 2 files changed, 47 insertions(+), 4 deletions(-)
>
Hello Muhammad,
could I ask for the patches rebase against for-next [1]?
Thank you much for your work!
David
[1] https://codeberg.org/linux-nfc/linux
^ permalink raw reply
* Re: [PATCH net] nfc: nci: validate packet length when parsing NCI 2.x RF interfaces
From: David Heidelberg @ 2026-06-21 16:46 UTC (permalink / raw)
To: Zijing Yin
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, oe-linux-nfc, netdev, linux-kernel, stable
In-Reply-To: <20260611162718.2301552-1-yzjaurora@gmail.com>
On 11/06/2026 18:27, Zijing Yin wrote:
> nci_core_init_rsp_packet_v2() parses the variable-length list of
> supported RF interfaces carried in an NCI 2.x CORE_INIT_RSP without ever
> validating the controller-supplied lengths against the size of the
> received packet.
>
> Each list entry is a (RF interface, RF extension count, RF extensions[])
> tuple. The loop walks the list using the per-entry extension count
> (rf_extension_cnt, up to 255) taken straight from the packet, so a
> malformed CORE_INIT_RSP can advance the read pointer far past the end of
> the skb data buffer. The stored interface count is clamped to
> NCI_MAX_SUPPORTED_RF_INTERFACES so the write side is bounded, but the
> read side runs off the end of the buffer.
>
> A malformed CORE_INIT_RSP from the controller, also reachable from user
> space through the virtual NCI device (CONFIG_NFC_VIRTUAL_NCI) once the
> device has entered NCI 2.x mode, therefore makes the parser read past the
> end of the response buffer while walking the interface list, copying the
> out-of-bounds bytes into ndev->supported_rf_interfaces[].
>
> Reject responses shorter than the fixed part of the structure, and make
> sure each interface entry and its extension bytes lie within the received
> packet before dereferencing them. A truncated or malformed list is
> treated as a syntax error, which fails the CORE_INIT request instead of
> reading out of bounds.
>
> Fixes: bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence")
> Cc: stable@vger.kernel.org
> Signed-off-by: Zijing Yin <yzjaurora@gmail.com>
> ---
> net/nfc/nci/rsp.c | 18 +++++++++++++++++-
> 1 file changed, 17 insertions(+), 1 deletion(-)
>
Hello Zijing,
in meanwhile [1] got merged earlier patch, which also seems to address the issue.
Could you review that the issue is now fixed for you?
The NFC tree is here [2].
Thank you and sorry for the late reply
David
[1] https://lore.kernel.org/all/20260527052625.3309581-1-yun.zhou@windriver.com/
[2] https://codeberg.org/linux-nfc/linux
^ permalink raw reply
* Re: [PATCH net v4] nfc: llcp: bound SNL TLV parsing to the skb and add length checks
From: David Heidelberg @ 2026-06-21 16:34 UTC (permalink / raw)
To: Doruk Tan Ozturk, oe-linux-nfc
Cc: david.laight.linux, horms, netdev, linux-kernel
In-Reply-To: <20260609202543.42282-1-doruk@0sec.ai>
On Tue, 09 Jun 2026 22:25:43 +0200, Doruk Tan Ozturk wrote:
> nfc: llcp: bound SNL TLV parsing to the skb and add length checks
Applied, thanks!
[1/1] nfc: llcp: bound SNL TLV parsing to the skb and add length checks
commit: ed85d4cbbfaa4e630c5aa0d607348b42620d976b
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply
* [syzbot] [net?] WARNING in __ethtool_get_link_ksettings
From: syzbot @ 2026-06-21 15:22 UTC (permalink / raw)
To: andrew, davem, edumazet, horms, kuba, linux-kernel, netdev,
pabeni, syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: 4fa3f5fabb30 Add linux-next specific files for 20260616
git tree: linux-next
console output: https://syzkaller.appspot.com/x/log.txt?x=1039ffec580000
kernel config: https://syzkaller.appspot.com/x/.config?x=6c414e1864e61ef6
dashboard link: https://syzkaller.appspot.com/bug?extid=09da62a8b78959ceb8bb
compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
Unfortunately, I don't have any reproducer for this issue yet.
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/bf5b803a695d/disk-4fa3f5fa.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/47871e7c589e/vmlinux-4fa3f5fa.xz
kernel image: https://storage.googleapis.com/syzbot-assets/53cd9ef32a2b/bzImage-4fa3f5fa.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+09da62a8b78959ceb8bb@syzkaller.appspotmail.com
netlink: 'syz.3.4736': attribute type 10 has an invalid length.
------------[ cut here ]------------
rtmutex deadlock detected
WARNING: kernel/locking/rtmutex.c:1698 at rt_mutex_handle_deadlock+0x21/0xb0 kernel/locking/rtmutex.c:1698, CPU#0: syz.3.4736/19861
Modules linked in:
CPU: 0 UID: 0 PID: 19861 Comm: syz.3.4736 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)}
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
RIP: 0010:rt_mutex_handle_deadlock+0x21/0xb0 kernel/locking/rtmutex.c:1698
Code: 90 90 90 90 90 90 90 90 90 41 57 41 56 41 55 41 54 53 83 ff dd 0f 85 81 00 00 00 48 89 f7 e8 16 40 01 00 48 8d 3d 3f 2f 5d 04 <67> 48 0f b9 3a 4c 8d 3d 00 00 00 00 65 48 8b 1d 13 2d 42 07 4c 8d
RSP: 0018:ffffc90003d760f0 EFLAGS: 00010286
RAX: 0000000080000000 RBX: 00000000ffffffdd RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffffff8baa3a60 RDI: ffffffff8f8fd220
RBP: ffffc90003d762a0 R08: ffffffff8f8c5ff7 R09: 1ffffffff1f18bfe
R10: dffffc0000000000 R11: fffffbfff1f18bff R12: 1ffff920007aec2c
R13: ffffffff8b329d22 R14: ffff88805dbb1008 R15: dffffc0000000000
FS: 00007fd5506ae6c0(0000) GS:ffff888125ed3000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 000000003cd08000 CR4: 00000000003526f0
Call Trace:
<TASK>
__rt_mutex_slowlock kernel/locking/rtmutex.c:1760 [inline]
__rt_mutex_slowlock_locked kernel/locking/rtmutex.c:1787 [inline]
rt_mutex_slowlock+0x73c/0x780 kernel/locking/rtmutex.c:1827
__rt_mutex_lock kernel/locking/rtmutex.c:1842 [inline]
__mutex_lock_common kernel/locking/rtmutex_api.c:560 [inline]
mutex_lock_nested+0x168/0x1d0 kernel/locking/rtmutex_api.c:578
netdev_lock include/linux/netdevice.h:2846 [inline]
netdev_lock_ops include/net/netdev_lock.h:42 [inline]
__ethtool_get_link_ksettings+0x109/0x250 net/ethtool/ioctl.c:463
bond_update_speed_duplex drivers/net/bonding/bond_main.c:801 [inline]
bond_slave_netdev_event drivers/net/bonding/bond_main.c:3982 [inline]
bond_netdev_event+0x643/0xf80 drivers/net/bonding/bond_main.c:4089
notifier_call_chain+0x1a5/0x3d0 kernel/notifier.c:85
call_netdevice_notifiers_extack net/core/dev.c:2289 [inline]
call_netdevice_notifiers net/core/dev.c:2303 [inline]
__dev_notify_flags+0x1aa/0x310 net/core/dev.c:9792
netif_change_flags+0xde/0x1b0 net/core/dev.c:9821
dev_change_flags+0x128/0x260 net/core/dev_api.c:68
vlan_device_event+0x1b4e/0x1f00 net/8021q/vlan.c:494
notifier_call_chain+0x1a5/0x3d0 kernel/notifier.c:85
call_netdevice_notifiers_extack net/core/dev.c:2289 [inline]
call_netdevice_notifiers net/core/dev.c:2303 [inline]
netif_open+0x10f/0x190 net/core/dev.c:1730
dev_open+0x101/0x220 net/core/dev_api.c:202
bond_enslave+0xeb2/0x3b70 drivers/net/bonding/bond_main.c:2089
do_set_master+0x563/0x720 net/core/rtnetlink.c:3012
do_setlink+0xe7b/0x4670 net/core/rtnetlink.c:3214
rtnl_changelink net/core/rtnetlink.c:3841 [inline]
__rtnl_newlink net/core/rtnetlink.c:4014 [inline]
rtnl_newlink+0x15c2/0x1bd0 net/core/rtnetlink.c:4151
rtnetlink_rcv_msg+0x802/0xc00 net/core/rtnetlink.c:7068
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7f5/0x990 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:785
__sock_sendmsg net/socket.c:800 [inline]
____sys_sendmsg+0x565/0x870 net/socket.c:2702
___sys_sendmsg+0x2a5/0x360 net/socket.c:2756
__sys_sendmsg net/socket.c:2788 [inline]
__do_sys_sendmsg net/socket.c:2793 [inline]
__se_sys_sendmsg net/socket.c:2791 [inline]
__x64_sys_sendmsg+0x1b7/0x290 net/socket.c:2791
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fd55245ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fd5506ae028 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007fd5526d5fa0 RCX: 00007fd55245ce59
RDX: 0000000000008084 RSI: 0000200000000600 RDI: 0000000000000003
RBP: 00007fd5524f2d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fd5526d6038 R14: 00007fd5526d5fa0 R15: 00007ffdbadd34a8
</TASK>
----------------
Code disassembly (best guess):
0: 90 nop
1: 90 nop
2: 90 nop
3: 90 nop
4: 90 nop
5: 90 nop
6: 90 nop
7: 90 nop
8: 90 nop
9: 41 57 push %r15
b: 41 56 push %r14
d: 41 55 push %r13
f: 41 54 push %r12
11: 53 push %rbx
12: 83 ff dd cmp $0xffffffdd,%edi
15: 0f 85 81 00 00 00 jne 0x9c
1b: 48 89 f7 mov %rsi,%rdi
1e: e8 16 40 01 00 call 0x14039
23: 48 8d 3d 3f 2f 5d 04 lea 0x45d2f3f(%rip),%rdi # 0x45d2f69
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: 4c 8d 3d 00 00 00 00 lea 0x0(%rip),%r15 # 0x36
36: 65 48 8b 1d 13 2d 42 mov %gs:0x7422d13(%rip),%rbx # 0x7422d51
3d: 07
3e: 4c rex.WR
3f: 8d .byte 0x8d
---
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 syzkaller@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
^ permalink raw reply
* Re: "ip help" output is an error
From: Stephen Hemminger @ 2026-06-21 15:21 UTC (permalink / raw)
To: Dmitri Seletski; +Cc: netdev
In-Reply-To: <62f09fe8-899c-4d22-b7a1-67e2745613df@gmail.com>
On Sat, 20 Jun 2026 10:36:31 +0100
Dmitri Seletski <drjoms@gmail.com> wrote:
> Hello iproute2 maintainers,
>
> I am reporting an inconsistency regarding the exit status of the ip help
> command.
>
> Current Behavior:
> When running ip help, the command prints the help documentation to
> stdout, but exits with a non-zero status (error). This causes issues in
> shell scripts that rely on exit codes for control flow.
>
> Steps to reproduce:
> bash
>
> # This returns "FAIL" because the exit code is non-zero
> if ip help > /dev/null; then
> echo "SUCCESS"
> else
> echo "FAIL"
> fi
>
> Expected Behavior:
> Since the command successfully performs the requested task (displaying
> help information) and does not encounter a system error, it should
> return an exit code of 0.
>
> Context:
> This behavior breaks standard Bash logic for automation. For example:
> ip help && echo "This will not execute"
>
> "ip help |grep br" - this will bring no result.
>
> Current version tested: iproute2-6.19.0
>
> Thank you for your time and for maintaining this tool.
>
> Regards,
> Dmitri Seletski
>
>
Yes iproute2 doesn't do a great job of handling error codes
with usage vs help. Its a bug and no one has bothered to fix it.
^ permalink raw reply
* Re: [PATCH v2] net: add sock_open() with flags for socket creation
From: Andrew Lunn @ 2026-06-21 13:59 UTC (permalink / raw)
To: David Laight; +Cc: Alex Goltsev, davem, netdev, linux-kernel, Al Viro
In-Reply-To: <20260621135746.067a93be@pumpkin>
On Sun, Jun 21, 2026 at 01:57:46PM +0100, David Laight wrote:
> On Sun, 21 Jun 2026 14:05:40 +0300
> Alex Goltsev <sasha.goltsev777@gmail.com> wrote:
>
> > From a9316957e594708dfb4258ad968fe88666c9b736 Mon Sep 17 00:00:00 2001
> > From: 0-x-0-0 <sasha.goltsev777@gmail.com>
> > Date: Sun, 21 Jun 2026 13:24:29 +0300
> > Subject: [PATCH v2] net: add sock_open() with flags for socket creation
>
> A) There is no info here.
> B) You've not said why this is of any use.
> C) It isn't a bug fix so would go into net-next
> D) net-next is closed.
E) The patch has had all its whitespace corrupted.
Please "submit" the patch to yourself and ensure you can cleanly apply
it.
Andrew
^ permalink raw reply
* Re: [Intel-wired-lan] e1000e: Report link down after "Detected Hardware Unit Hang" ?
From: Ruinskiy, Dima @ 2026-06-21 13:22 UTC (permalink / raw)
To: Andrew Lunn, Helge Deller, Helge Deller
Cc: Tony Nguyen, Przemek Kitszel, intel-wired-lan, netdev
In-Reply-To: <d86c0dd8-8bd8-495a-b750-2a0036fbbee4@lunn.ch>
On 17/06/2026 0:59, Andrew Lunn wrote:
>> This does not seem like the right direction to me.
>>
>> The "Detected Hardware Unit Hang" print does not indicate that the interface
>> is dead, but that the transmitter is stalled.
>>
>> This can be due to an unusually high load, or a HW fault / race condition
>> with another component, etc.
>>
>> When a hang is detected, the transmitter is stopped with netif_stop_queue()
>> and eventually ndo_tx_timeout triggers a full reset to the device, which in
>> many cases recovers it from the hang.
>
> Does a full reset cause the link to be negotiated again? If so, there
> is no harm in setting the carrier down. If the reset is successful,
> the carrier will be restored. However, if the reset does not recover
> the system, does the carrier say down?
>
> Andrew
>
The way it is written - a reset triggered by the Tx timeout path will go
through e1000e_reinit_locked(), which calls e1000e_down() followed by
e1000e_up().
e1000e_down() calls netif_carrier_off() at the start, and e1000e_reset()
later. e1000e_up() triggers a link state recheck, which should restore
the carrier.
So if everything works as it should, the change proposed here would be
both redundant and unnecessary. However, we have been getting reports of
these unrecoverable hangs from time-to-time, so I suspect things do not
always work as they should.
There is one issue under investigation at present, where a persistent
hang was reported following an aborted hibernation attempt. We are
testing a patch against it.
I did not see anything in the original description of this report tying
the hang to a power state change, but I will happily share the patch
once we get preliminary positive results.
--Dima
^ permalink raw reply
* Re: [PATCH v2] net: add sock_open() with flags for socket creation
From: David Laight @ 2026-06-21 12:57 UTC (permalink / raw)
To: Alex Goltsev; +Cc: davem, netdev, linux-kernel, Al Viro
In-Reply-To: <CAEKmD4K-v_srabQDJCfqaqA6vssk-Hg-mLHEdTsphTBLmQVjnw@mail.gmail.com>
On Sun, 21 Jun 2026 14:05:40 +0300
Alex Goltsev <sasha.goltsev777@gmail.com> wrote:
> From a9316957e594708dfb4258ad968fe88666c9b736 Mon Sep 17 00:00:00 2001
> From: 0-x-0-0 <sasha.goltsev777@gmail.com>
> Date: Sun, 21 Jun 2026 13:24:29 +0300
> Subject: [PATCH v2] net: add sock_open() with flags for socket creation
A) There is no info here.
B) You've not said why this is of any use.
C) It isn't a bug fix so would go into net-next
D) net-next is closed.
David
>
> ---
> Changes in V2:
> - Replaced the use of plain integer constants for flags with proper
> enums to improve readability and type safety.
> - `sock_open` is intentionally left as a regular exported symbol rather
> than being moved to a header as `static inline`. This is because it
> dereferences `current->nsproxy->net_ns`, which would require pulling
> in heavy headers like <linux/sched.h> and <linux/nsproxy.h> into the
> already widely-used <linux/net.h>, causing unnecessary header bloat
> and potential circular dependencies.
> - Introduced two new creation flags for specialized use cases within
> kernel modules:
>
> * SOCK_CREATE_NOLSM: This flag allows a kernel module to bypass
> LSM hooks during socket creation. This
> enables a micro-optimization for kernel-internal sockets where
> the security check is known *a priori* to be a no-op (e.g., for
> specific configurations or high-performance paths).
> This is safe because the API is restricted to in-kernel (LKM)
> contexts only, and does not weaken the security boundary for
> user-triggered socket creation.
>
> * SOCK_CREATE_NOWARN: This flag suppresses the standard warning
> messages on creation failure. This is useful for callers in the
> kernel that probe for protocol support and handle the error
> gracefully, without wanting to pollute the kernel log with
> misleading warnings.
>
> Signed-off-by: Alexander Goltsev <sasha.goltsev777@gmail.com>
> ---
> include/linux/net.h | 22 ++++++++
> net/socket.c | 133 +++++++++++++++++++++++++++++++++++++-------
> 2 files changed, 134 insertions(+), 21 deletions(-)
>
> diff --git a/include/linux/net.h b/include/linux/net.h
> index f268f395c..6367c00db 100644
> --- a/include/linux/net.h
> +++ b/include/linux/net.h
> @@ -116,6 +116,22 @@ enum sock_shutdown_cmd {
> SHUT_RDWR,
> };
> +/**
> + * enum sock_create_flags - socket creation flags
> + * @SOCK_CREATE_KERN: creates a kernel socket
> + * @SOCK_CREATE_USER: creates a regular socket
> + * @SOCK_CREATE_LITE: creates a lite socket
> + * @SOCK_CREATE_NOLSM: disables LSM
> + * @SOCK_CREATE_NOWARN: disables warning
> + */
> +enum sock_create_flags {
> + SOCK_CREATE_KERN = BIT(0),
> + SOCK_CREATE_USER = BIT(1),
> + SOCK_CREATE_LITE = BIT(2),
> + SOCK_CREATE_NOLSM = BIT(3),
> + SOCK_CREATE_NOWARN = BIT(4),
> +};
> +
> struct socket_wq {
> /* Note: wait MUST be first field of socket_wq */
> wait_queue_head_t wait;
> @@ -275,6 +291,12 @@ void sock_unregister(int family);
> bool sock_is_registered(int family);
> int __sock_create(struct net *net, int family, int type, int proto,
> struct socket **res, int kern);
> +int __sock_create_flags(struct net *net, int family, int type, int protocol,
> + struct socket **res, int kern, int flags);
> +int __sock_create_lite_flags(int family, int type, int protocol,
> + struct socket **res, int flags);
> +int sock_open(struct net *net, int family,
> + int type, int protocol, struct socket **res, int flags);
> int sock_create(int family, int type, int proto, struct socket **res);
> int sock_create_kern(struct net *net, int family, int type, int proto,
> struct socket **res);
> int sock_create_lite(int family, int type, int proto, struct socket **res);
> diff --git a/net/socket.c b/net/socket.c
> index 63c69a0fa..2359fd5bf 100644
> --- a/net/socket.c
> +++ b/net/socket.c
> @@ -1425,26 +1425,28 @@ static long sock_ioctl(struct file *file,
> unsigned cmd, unsigned long arg)
> }
> /**
> - * sock_create_lite - creates a socket
> + * __sock_create_lite_flags - creates a socket (with flags)
> * @family: protocol family (AF_INET, ...)
> * @type: communication type (SOCK_STREAM, ...)
> * @protocol: protocol (0, ...)
> * @res: new socket
> + * @flags: defines socket creation flags
> *
> * Creates a new socket and assigns it to @res, passing through LSM.
> * The new socket initialization is not complete, see kernel_accept().
> * Returns 0 or an error. On failure @res is set to %NULL.
> * This function internally uses GFP_KERNEL.
> */
> -
> -int sock_create_lite(int family, int type, int protocol, struct socket **res)
> +int __sock_create_lite_flags(int family, int type, int protocol,
> struct socket **res, int flags)
> {
> int err;
> struct socket *sock = NULL;
> - err = security_socket_create(family, type, protocol, 1);
> - if (err)
> - goto out;
> + if (!(flags & SOCK_CREATE_NOLSM)) {
> + err = security_socket_create(family, type, protocol, 1);
> + if (err)
> + goto out;
> + }
> sock = sock_alloc();
> if (!sock) {
> @@ -1453,9 +1455,11 @@ int sock_create_lite(int family, int type, int
> protocol, struct socket **res)
> }
> sock->type = type;
> - err = security_socket_post_create(sock, family, type, protocol, 1);
> - if (err)
> - goto out_release;
> + if (!(flags & SOCK_CREATE_NOLSM)) {
> + err = security_socket_post_create(sock, family, type, protocol, 1);
> + if (err)
> + goto out_release;
> + }
> out:
> *res = sock;
> @@ -1465,6 +1469,25 @@ int sock_create_lite(int family, int type, int
> protocol, struct socket **res)
> sock = NULL;
> goto out;
> }
> +EXPORT_SYMBOL(__sock_create_lite_flags);
> +
> +/**
> + * sock_create_lite - creates a socket
> + * @family: protocol family (AF_INET, ...)
> + * @type: communication type (SOCK_STREAM, ...)
> + * @protocol: protocol (0, ...)
> + * @res: new socket
> + *
> + * Creates a new socket and assigns it to @res, passing through LSM.
> + * The new socket initialization is not complete, see kernel_accept().
> + * Returns 0 or an error. On failure @res is set to %NULL.
> + * This function internally uses GFP_KERNEL.
> + */
> +
> +int sock_create_lite(int family, int type, int protocol, struct socket **res)
> +{
> + return __sock_create_lite_flags(family, type, protocol, res, 0);
> +}
> EXPORT_SYMBOL(sock_create_lite);
> /* No kernel lock held - perfect */
> @@ -1563,22 +1586,23 @@ int sock_wake_async(struct socket_wq *wq, int
> how, int band)
> EXPORT_SYMBOL(sock_wake_async);
> /**
> - * __sock_create - creates a socket
> + * __sock_create_flags - creates a socket (with flags)
> * @net: net namespace
> * @family: protocol family (AF_INET, ...)
> * @type: communication type (SOCK_STREAM, ...)
> * @protocol: protocol (0, ...)
> * @res: new socket
> * @kern: boolean for kernel space sockets
> + * @flags: defines socket creation flags
> *
> * Creates a new socket and assigns it to @res, passing through LSM.
> * Returns 0 or an error. On failure @res is set to %NULL. @kern must
> * be set to true if the socket resides in kernel space.
> * This function internally uses GFP_KERNEL.
> */
> -
> -int __sock_create(struct net *net, int family, int type, int protocol,
> - struct socket **res, int kern)
> +int __sock_create_flags(struct net *net, int family,
> + int type, int protocol, struct socket **res,
> + int kern, int flags)
> {
> int err;
> struct socket *sock;
> @@ -1598,14 +1622,18 @@ int __sock_create(struct net *net, int family,
> int type, int protocol,
> deadlock in module load.
> */
> if (family == PF_INET && type == SOCK_PACKET) {
> - pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
> + if (!(flags & SOCK_CREATE_NOWARN)) {
> + pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
> current->comm);
> + }
> family = PF_PACKET;
> }
> - err = security_socket_create(family, type, protocol, kern);
> - if (err)
> - return err;
> + if (!(flags & SOCK_CREATE_NOLSM)) {
> + err = security_socket_create(family, type, protocol, kern);
> + if (err)
> + return err;
> + }
> /*
> * Allocate the socket and allow the family to set things up. if
> @@ -1614,7 +1642,8 @@ int __sock_create(struct net *net, int family,
> int type, int protocol,
> */
> sock = sock_alloc();
> if (!sock) {
> - net_warn_ratelimited("socket: no more sockets\n");
> + if (!(flags & SOCK_CREATE_NOWARN))
> + net_warn_ratelimited("socket: no more sockets\n");
> return -ENFILE; /* Not exactly a match, but its the
> closest posix thing */
> }
> @@ -1671,9 +1700,12 @@ int __sock_create(struct net *net, int family,
> int type, int protocol,
> * module can have its refcnt decremented
> */
> module_put(pf->owner);
> - err = security_socket_post_create(sock, family, type, protocol, kern);
> - if (err)
> - goto out_sock_release;
> +
> + if (!(flags & SOCK_CREATE_NOLSM)) {
> + err = security_socket_post_create(sock, family, type, protocol, kern);
> + if (err)
> + goto out_sock_release;
> + }
> *res = sock;
> return 0;
> @@ -1691,6 +1723,28 @@ int __sock_create(struct net *net, int family,
> int type, int protocol,
> rcu_read_unlock();
> goto out_sock_release;
> }
> +EXPORT_SYMBOL(__sock_create_flags);
> +
> +/**
> + * __sock_create - creates a socket
> + * @net: net namespace
> + * @family: protocol family (AF_INET, ...)
> + * @type: communication type (SOCK_STREAM, ...)
> + * @protocol: protocol (0, ...)
> + * @res: new socket
> + * @kern: boolean for kernel space sockets
> + *
> + * Creates a new socket and assigns it to @res, passing through LSM.
> + * Returns 0 or an error. On failure @res is set to %NULL. @kern must
> + * be set to true if the socket resides in kernel space.
> + * This function internally uses GFP_KERNEL.
> + */
> +
> +int __sock_create(struct net *net, int family, int type, int protocol,
> + struct socket **res, int kern)
> +{
> + return __sock_create_flags(net, family, type, protocol, res, kern, 0);
> +}
> EXPORT_SYMBOL(__sock_create);
> /**
> @@ -1710,6 +1764,43 @@ int sock_create(int family, int type, int
> protocol, struct socket **res)
> }
> EXPORT_SYMBOL(sock_create);
> +/**
> + * sock_open - creates a socket (with flags)
> + * @net: net namespace (may be NULL in non-SOCK_CREATE_KERN modes)
> + * @family: protocol family (AF_INET, ...)
> + * @type: communication type (SOCK_STREAM, ...)
> + * @protocol: protocol (0, ...)
> + * @res: new socket
> + * @flags: socket creation flags
> + *
> + * Unified entry point for socket creation with flags.
> + * Returns 0 or an error. This function internally uses GFP_KERNEL.
> + */
> +int sock_open(struct net *net, int family,
> + int type, int protocol, struct socket **res,
> + int flags)
> +{
> + int type_bits = flags & (SOCK_CREATE_KERN | SOCK_CREATE_USER |
> SOCK_CREATE_LITE);
> + int optional_flags = flags & ~(SOCK_CREATE_KERN | SOCK_CREATE_USER |
> SOCK_CREATE_LITE);
> +
> + if (type_bits == 0 || (type_bits & (type_bits - 1)) != 0)
> + return -EINVAL;
> +
> + if (optional_flags & ~(SOCK_CREATE_NOLSM | SOCK_CREATE_NOWARN))
> + return -EINVAL;
> +
> + switch (type_bits) {
> + case SOCK_CREATE_KERN: return __sock_create_flags(net, family, type, protocol,
> + res, 1, optional_flags);
> + case SOCK_CREATE_USER: return __sock_create_flags(current->nsproxy->net_ns,
> + family, type, protocol, res, 0, optional_flags);
> + case SOCK_CREATE_LITE: return __sock_create_lite_flags(family,
> + type, protocol, res, optional_flags);
> + default: return -EINVAL;
> + }
> +}
> +EXPORT_SYMBOL(sock_open);
> +
> /**
> * sock_create_kern - creates a socket (kernel space)
> * @net: net namespace
^ permalink raw reply
* [PATCH 2/2 net-next,v2] i40e: add devlink parameter for Flow Director ATR sample rate
From: mheib @ 2026-06-21 12:56 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, jiri, davem, edumazet, kuba, pabeni, horms, corbet,
anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev,
Mohammad Heib
In-Reply-To: <20260621125644.253844-1-mheib@redhat.com>
From: Mohammad Heib <mheib@redhat.com>
The i40e driver uses Flow Director ATR to periodically update flow
steering information for active TCP flows. The update frequency is
currently controlled by I40E_DEFAULT_ATR_SAMPLE_RATE and is fixed at
driver build time.
On systems with a large number of queues and high-rate TCP workloads,
the default sampling interval can result in frequent Flow Director
reprogramming for long-lived flows.
The amount of TCP packet reordering observed on some systems is
sensitive to the ATR sampling interval. Increasing the interval reduces
Flow Director programming activity and can significantly reduce the
associated reordering.
Since the optimal sampling interval depends on the workload and system
configuration, a single fixed value is not suitable for all deployments.
Add a devlink parameter to allow administrators to tune the ATR sample
rate at runtime without rebuilding the driver or disabling ATR
functionality entirely.
Signed-off-by: Mohammad Heib <mheib@redhat.com>
---
Documentation/networking/devlink/i40e.rst | 20 +++++++++++
.../net/ethernet/intel/i40e/i40e_devlink.c | 36 +++++++++++++++++++
2 files changed, 56 insertions(+)
diff --git a/Documentation/networking/devlink/i40e.rst b/Documentation/networking/devlink/i40e.rst
index 51c887f0dc83..2cea98b631ba 100644
--- a/Documentation/networking/devlink/i40e.rst
+++ b/Documentation/networking/devlink/i40e.rst
@@ -40,6 +40,26 @@ Parameters
The default value is ``0`` (internal calculation is used).
+.. list-table:: Driver specific parameters implemented
+ :widths: 5 5 90
+
+ * - Name
+ - Mode
+ - Description
+ * - ``atr_sample_rate``
+ - runtime
+ - Controls how frequently Flow Director ATR updates flow steering
+ information for active TCP flows.
+
+ ATR programs Flow Director entries based on sampled transmitted
+ packets. The sampling interval is specified as the number of
+ transmitted packets between ATR updates.
+
+ Lower values increase Flow Director programming activity, while
+ higher values reduce the update frequency.
+
+ Setting to ``0`` disables ATR sampling (no filters will be programmed)
+ The default value is ``20``.
Info versions
=============
diff --git a/drivers/net/ethernet/intel/i40e/i40e_devlink.c b/drivers/net/ethernet/intel/i40e/i40e_devlink.c
index 229179ccc131..cf487efdd803 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_devlink.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_devlink.c
@@ -33,12 +33,48 @@ static int i40e_max_mac_per_vf_get(struct devlink *devlink,
return 0;
}
+static int i40e_atr_sample_rate_set(struct devlink *devlink,
+ u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct i40e_pf *pf = devlink_priv(devlink);
+ u32 sample_rate = ctx->val.vu32;
+
+ pf->atr_sample_rate = sample_rate;
+ return 0;
+}
+
+static int i40e_atr_sample_rate_get(struct devlink *devlink,
+ u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct i40e_pf *pf = devlink_priv(devlink);
+
+ ctx->val.vu32 = pf->atr_sample_rate;
+
+ return 0;
+}
+
+enum i40e_dl_param_id {
+ I40E_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
+ I40E_DEVLINK_PARAM_ID_ATR_SAMPLE_RATE,
+};
+
static const struct devlink_param i40e_dl_params[] = {
DEVLINK_PARAM_GENERIC(MAX_MAC_PER_VF,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
i40e_max_mac_per_vf_get,
i40e_max_mac_per_vf_set,
NULL),
+ DEVLINK_PARAM_DRIVER(I40E_DEVLINK_PARAM_ID_ATR_SAMPLE_RATE,
+ "atr_sample_rate",
+ DEVLINK_PARAM_TYPE_U32,
+ BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+ i40e_atr_sample_rate_get,
+ i40e_atr_sample_rate_set,
+ NULL),
};
static void i40e_info_get_dsn(struct i40e_pf *pf, char *buf, size_t len)
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2 net-next,v1] i40e: move ATR sample rate from ring to PF level
From: mheib @ 2026-06-21 12:56 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, jiri, davem, edumazet, kuba, pabeni, horms, corbet,
anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev,
Mohammad Heib
From: Mohammad Heib <mheib@redhat.com>
The ATR sample rate is currently stored per-ring and initialized when each
TX ring is configured. Since the sample rate is a global policy that
applies uniformly across all rings, it makes more sense to store it at
the PF level.
Move atr_sample_rate from struct i40e_ring to struct i40e_pf and initialize
it once during i40e_sw_init(). Update i40e_atr() to reference the PF-level
field. Change atr_count from u8 to u32 to match the sample rate type.
Signed-off-by: Mohammad Heib <mheib@redhat.com>
---
drivers/net/ethernet/intel/i40e/i40e.h | 1 +
drivers/net/ethernet/intel/i40e/i40e_main.c | 9 +++------
drivers/net/ethernet/intel/i40e/i40e_txrx.c | 4 ++--
drivers/net/ethernet/intel/i40e/i40e_txrx.h | 3 +--
4 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 1b6a8fbaa648..88eb40ee45f0 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -487,6 +487,7 @@ struct i40e_pf {
u16 rss_size_max; /* HW defined max RSS queues */
u16 fdir_pf_filter_count; /* num of guaranteed filters for this PF */
u16 num_alloc_vsi; /* num VSIs this driver supports */
+ u32 atr_sample_rate;
bool wol_en;
struct hlist_head fdir_filter_list;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index d59750c490f4..9695d160bc59 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -3457,12 +3457,7 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring)
ring->xsk_pool = i40e_xsk_pool(ring);
/* some ATR related tx ring init */
- if (test_bit(I40E_FLAG_FD_ATR_ENA, vsi->back->flags)) {
- ring->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
- ring->atr_count = 0;
- } else {
- ring->atr_sample_rate = 0;
- }
+ ring->atr_count = 0;
/* configure XPS */
i40e_config_xps_tx_ring(ring);
@@ -12745,6 +12740,8 @@ static int i40e_sw_init(struct i40e_pf *pf)
}
}
+ pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
+
if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
(pf->hw.func_caps.fd_filters_best_effort > 0)) {
set_bit(I40E_FLAG_FD_ATR_ENA, pf->flags);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 61525ab7d21e..da94cb2ce94d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -2882,7 +2882,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
return;
/* if sampling is disabled do nothing */
- if (!tx_ring->atr_sample_rate)
+ if (!pf->atr_sample_rate)
return;
/* Currently only IPv4/IPv6 with TCP is supported */
@@ -2934,7 +2934,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (!th->fin &&
!th->syn &&
!th->rst &&
- (tx_ring->atr_count < tx_ring->atr_sample_rate))
+ (tx_ring->atr_count < pf->atr_sample_rate))
return;
tx_ring->atr_count = 0;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index bb741ff3e5f2..be587f804e7a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -372,8 +372,7 @@ struct i40e_ring {
u16 next_to_clean;
u16 xdp_tx_active;
- u8 atr_sample_rate;
- u8 atr_count;
+ u32 atr_count;
bool ring_active; /* is ring online or not */
bool arm_wb; /* do something to arm write back */
--
2.53.0
^ permalink raw reply related
* Fwd: HTML message rejected: qcom_ppe: direct (non-DSA) MAC port - host-bound RX dropped at EDMA (src_info_type != PORTID)?
From: perceival percy @ 2026-06-21 11:18 UTC (permalink / raw)
Cc: netdev
In-Reply-To: <1782040471-27241-mlmmj-64bd9d5a@vger.kernel.org>
Hi Luo Jie, netdev,
I'm porting the GL.iNet GL-BE9300 (IPQ5332) to mainline OpenWrt and have run
into what looks like a gap in qcom_ppe for a direct (non-DSA) MAC port. The
question is at the bottom; I'm happy to provide the full DT, register dumps, or
test patches.
Setup
-----
SoC: IPQ5332, kernel 6.12, drivers/net/ethernet/qualcomm/ppe. Two ports:
- port@1: 10G SerDes, DSA conduit to an external RTL8372N switch (eth0). RX
works fine; the DSA tagger demuxes the switch ports.
- port@2: 2.5G, direct on-board RTL8221B PHY (phy-mode = "2500base-x"), used as
a standalone L3 "WAN" (eth1, no bridge, no DSA). RX is broken.
Symptom (port@2)
----------------
Link comes up at 2.5G/full. The MAC receives - the PPE ingress counters
advance:
PORT_RX_CNT ... 4/1(port=0002)
VPORT_RX_CNT ... 4/0(port=0002)
...but eth1 rx_packets stays 0, and the EDMA RX-ring error stat shows the drop:
/sys/kernel/debug/ppe/edma/stats/rx_ring_stats:
rxdesc[15]:src_port_inval_type = 1
So the frame reaches the EDMA RX descriptor ring, but in edma_rx_get_src_dev()
the source is not tagged as PORTID:
if ((src_info & EDMA_RXDESC_SRCINFO_TYPE_MASK) ==
EDMA_RXDESC_SRCINFO_TYPE_PORTID) {
src_port_num = src_info & EDMA_RXDESC_PORTNUM_BITS;
...
} else {
... ++rxdesc_stats->src_port_inval_type; /* port@2: type == 0 */
return NULL; /* dropped */
}
so the netdev lookup never runs.
What I have checked
-------------------
- netdev_arr[port_id - 1] is populated for port@2 (edma_port_setup), so the
PORTID -> netdev mapping would resolve if the type were PORTID.
- port@2's L2 forwarding is the fallback path: PPE_L2_VP_PORT_TBL with
INVALID_VSI_FWD_EN = 1, DST_INFO = 0 (CPU port0) - set for every physical
port in ppe_config.c. That controls the destination; the RX descriptor
src_info type is still 0.
- Bridging eth1 (to give it a VSI) does not change it.
- port@1 (the DSA conduit) gets SRCINFO_TYPE_PORTID and is delivered correctly,
so the tagging clearly happens for that port's path.
Question
--------
What configures a port so the PPE tags its host-bound frames with
SRCINFO_TYPE_PORTID + src_port? Is there a per-port setup (an EG service code,
a source-port profile, a physical-vs-virtual-port distinction) that the
DSA-conduit path establishes but a standalone direct MAC port does not?
And more generally: is "deliver a direct MAC port's own ingress to the host
CPU" (a plain WAN/L3 port - no DSA, no bridge offload) an intended/supported
configuration today, or does the EDMA RX path assume all host-bound traffic
arrives via the DSA conduit or an offloaded bridge VSI?
Thanks,
Kamil Bienkiewicz (perceival on the OpenWrt forum)
^ permalink raw reply
* Re: [PATCH v2] net: add sock_open() with flags for socket creation
From: Alex Goltsev @ 2026-06-21 11:05 UTC (permalink / raw)
To: davem, netdev; +Cc: linux-kernel, Al Viro
In-Reply-To: <CAEKmD4KSvAGWEod3h8mPKQ-UYhKqakxfakt4gXrsU8sWuAO77g@mail.gmail.com>
From a9316957e594708dfb4258ad968fe88666c9b736 Mon Sep 17 00:00:00 2001
From: 0-x-0-0 <sasha.goltsev777@gmail.com>
Date: Sun, 21 Jun 2026 13:24:29 +0300
Subject: [PATCH v2] net: add sock_open() with flags for socket creation
---
Changes in V2:
- Replaced the use of plain integer constants for flags with proper
enums to improve readability and type safety.
- `sock_open` is intentionally left as a regular exported symbol rather
than being moved to a header as `static inline`. This is because it
dereferences `current->nsproxy->net_ns`, which would require pulling
in heavy headers like <linux/sched.h> and <linux/nsproxy.h> into the
already widely-used <linux/net.h>, causing unnecessary header bloat
and potential circular dependencies.
- Introduced two new creation flags for specialized use cases within
kernel modules:
* SOCK_CREATE_NOLSM: This flag allows a kernel module to bypass
LSM hooks during socket creation. This
enables a micro-optimization for kernel-internal sockets where
the security check is known *a priori* to be a no-op (e.g., for
specific configurations or high-performance paths).
This is safe because the API is restricted to in-kernel (LKM)
contexts only, and does not weaken the security boundary for
user-triggered socket creation.
* SOCK_CREATE_NOWARN: This flag suppresses the standard warning
messages on creation failure. This is useful for callers in the
kernel that probe for protocol support and handle the error
gracefully, without wanting to pollute the kernel log with
misleading warnings.
Signed-off-by: Alexander Goltsev <sasha.goltsev777@gmail.com>
---
include/linux/net.h | 22 ++++++++
net/socket.c | 133 +++++++++++++++++++++++++++++++++++++-------
2 files changed, 134 insertions(+), 21 deletions(-)
diff --git a/include/linux/net.h b/include/linux/net.h
index f268f395c..6367c00db 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -116,6 +116,22 @@ enum sock_shutdown_cmd {
SHUT_RDWR,
};
+/**
+ * enum sock_create_flags - socket creation flags
+ * @SOCK_CREATE_KERN: creates a kernel socket
+ * @SOCK_CREATE_USER: creates a regular socket
+ * @SOCK_CREATE_LITE: creates a lite socket
+ * @SOCK_CREATE_NOLSM: disables LSM
+ * @SOCK_CREATE_NOWARN: disables warning
+ */
+enum sock_create_flags {
+ SOCK_CREATE_KERN = BIT(0),
+ SOCK_CREATE_USER = BIT(1),
+ SOCK_CREATE_LITE = BIT(2),
+ SOCK_CREATE_NOLSM = BIT(3),
+ SOCK_CREATE_NOWARN = BIT(4),
+};
+
struct socket_wq {
/* Note: wait MUST be first field of socket_wq */
wait_queue_head_t wait;
@@ -275,6 +291,12 @@ void sock_unregister(int family);
bool sock_is_registered(int family);
int __sock_create(struct net *net, int family, int type, int proto,
struct socket **res, int kern);
+int __sock_create_flags(struct net *net, int family, int type, int protocol,
+ struct socket **res, int kern, int flags);
+int __sock_create_lite_flags(int family, int type, int protocol,
+ struct socket **res, int flags);
+int sock_open(struct net *net, int family,
+ int type, int protocol, struct socket **res, int flags);
int sock_create(int family, int type, int proto, struct socket **res);
int sock_create_kern(struct net *net, int family, int type, int proto,
struct socket **res);
int sock_create_lite(int family, int type, int proto, struct socket **res);
diff --git a/net/socket.c b/net/socket.c
index 63c69a0fa..2359fd5bf 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1425,26 +1425,28 @@ static long sock_ioctl(struct file *file,
unsigned cmd, unsigned long arg)
}
/**
- * sock_create_lite - creates a socket
+ * __sock_create_lite_flags - creates a socket (with flags)
* @family: protocol family (AF_INET, ...)
* @type: communication type (SOCK_STREAM, ...)
* @protocol: protocol (0, ...)
* @res: new socket
+ * @flags: defines socket creation flags
*
* Creates a new socket and assigns it to @res, passing through LSM.
* The new socket initialization is not complete, see kernel_accept().
* Returns 0 or an error. On failure @res is set to %NULL.
* This function internally uses GFP_KERNEL.
*/
-
-int sock_create_lite(int family, int type, int protocol, struct socket **res)
+int __sock_create_lite_flags(int family, int type, int protocol,
struct socket **res, int flags)
{
int err;
struct socket *sock = NULL;
- err = security_socket_create(family, type, protocol, 1);
- if (err)
- goto out;
+ if (!(flags & SOCK_CREATE_NOLSM)) {
+ err = security_socket_create(family, type, protocol, 1);
+ if (err)
+ goto out;
+ }
sock = sock_alloc();
if (!sock) {
@@ -1453,9 +1455,11 @@ int sock_create_lite(int family, int type, int
protocol, struct socket **res)
}
sock->type = type;
- err = security_socket_post_create(sock, family, type, protocol, 1);
- if (err)
- goto out_release;
+ if (!(flags & SOCK_CREATE_NOLSM)) {
+ err = security_socket_post_create(sock, family, type, protocol, 1);
+ if (err)
+ goto out_release;
+ }
out:
*res = sock;
@@ -1465,6 +1469,25 @@ int sock_create_lite(int family, int type, int
protocol, struct socket **res)
sock = NULL;
goto out;
}
+EXPORT_SYMBOL(__sock_create_lite_flags);
+
+/**
+ * sock_create_lite - creates a socket
+ * @family: protocol family (AF_INET, ...)
+ * @type: communication type (SOCK_STREAM, ...)
+ * @protocol: protocol (0, ...)
+ * @res: new socket
+ *
+ * Creates a new socket and assigns it to @res, passing through LSM.
+ * The new socket initialization is not complete, see kernel_accept().
+ * Returns 0 or an error. On failure @res is set to %NULL.
+ * This function internally uses GFP_KERNEL.
+ */
+
+int sock_create_lite(int family, int type, int protocol, struct socket **res)
+{
+ return __sock_create_lite_flags(family, type, protocol, res, 0);
+}
EXPORT_SYMBOL(sock_create_lite);
/* No kernel lock held - perfect */
@@ -1563,22 +1586,23 @@ int sock_wake_async(struct socket_wq *wq, int
how, int band)
EXPORT_SYMBOL(sock_wake_async);
/**
- * __sock_create - creates a socket
+ * __sock_create_flags - creates a socket (with flags)
* @net: net namespace
* @family: protocol family (AF_INET, ...)
* @type: communication type (SOCK_STREAM, ...)
* @protocol: protocol (0, ...)
* @res: new socket
* @kern: boolean for kernel space sockets
+ * @flags: defines socket creation flags
*
* Creates a new socket and assigns it to @res, passing through LSM.
* Returns 0 or an error. On failure @res is set to %NULL. @kern must
* be set to true if the socket resides in kernel space.
* This function internally uses GFP_KERNEL.
*/
-
-int __sock_create(struct net *net, int family, int type, int protocol,
- struct socket **res, int kern)
+int __sock_create_flags(struct net *net, int family,
+ int type, int protocol, struct socket **res,
+ int kern, int flags)
{
int err;
struct socket *sock;
@@ -1598,14 +1622,18 @@ int __sock_create(struct net *net, int family,
int type, int protocol,
deadlock in module load.
*/
if (family == PF_INET && type == SOCK_PACKET) {
- pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
+ if (!(flags & SOCK_CREATE_NOWARN)) {
+ pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
current->comm);
+ }
family = PF_PACKET;
}
- err = security_socket_create(family, type, protocol, kern);
- if (err)
- return err;
+ if (!(flags & SOCK_CREATE_NOLSM)) {
+ err = security_socket_create(family, type, protocol, kern);
+ if (err)
+ return err;
+ }
/*
* Allocate the socket and allow the family to set things up. if
@@ -1614,7 +1642,8 @@ int __sock_create(struct net *net, int family,
int type, int protocol,
*/
sock = sock_alloc();
if (!sock) {
- net_warn_ratelimited("socket: no more sockets\n");
+ if (!(flags & SOCK_CREATE_NOWARN))
+ net_warn_ratelimited("socket: no more sockets\n");
return -ENFILE; /* Not exactly a match, but its the
closest posix thing */
}
@@ -1671,9 +1700,12 @@ int __sock_create(struct net *net, int family,
int type, int protocol,
* module can have its refcnt decremented
*/
module_put(pf->owner);
- err = security_socket_post_create(sock, family, type, protocol, kern);
- if (err)
- goto out_sock_release;
+
+ if (!(flags & SOCK_CREATE_NOLSM)) {
+ err = security_socket_post_create(sock, family, type, protocol, kern);
+ if (err)
+ goto out_sock_release;
+ }
*res = sock;
return 0;
@@ -1691,6 +1723,28 @@ int __sock_create(struct net *net, int family,
int type, int protocol,
rcu_read_unlock();
goto out_sock_release;
}
+EXPORT_SYMBOL(__sock_create_flags);
+
+/**
+ * __sock_create - creates a socket
+ * @net: net namespace
+ * @family: protocol family (AF_INET, ...)
+ * @type: communication type (SOCK_STREAM, ...)
+ * @protocol: protocol (0, ...)
+ * @res: new socket
+ * @kern: boolean for kernel space sockets
+ *
+ * Creates a new socket and assigns it to @res, passing through LSM.
+ * Returns 0 or an error. On failure @res is set to %NULL. @kern must
+ * be set to true if the socket resides in kernel space.
+ * This function internally uses GFP_KERNEL.
+ */
+
+int __sock_create(struct net *net, int family, int type, int protocol,
+ struct socket **res, int kern)
+{
+ return __sock_create_flags(net, family, type, protocol, res, kern, 0);
+}
EXPORT_SYMBOL(__sock_create);
/**
@@ -1710,6 +1764,43 @@ int sock_create(int family, int type, int
protocol, struct socket **res)
}
EXPORT_SYMBOL(sock_create);
+/**
+ * sock_open - creates a socket (with flags)
+ * @net: net namespace (may be NULL in non-SOCK_CREATE_KERN modes)
+ * @family: protocol family (AF_INET, ...)
+ * @type: communication type (SOCK_STREAM, ...)
+ * @protocol: protocol (0, ...)
+ * @res: new socket
+ * @flags: socket creation flags
+ *
+ * Unified entry point for socket creation with flags.
+ * Returns 0 or an error. This function internally uses GFP_KERNEL.
+ */
+int sock_open(struct net *net, int family,
+ int type, int protocol, struct socket **res,
+ int flags)
+{
+ int type_bits = flags & (SOCK_CREATE_KERN | SOCK_CREATE_USER |
SOCK_CREATE_LITE);
+ int optional_flags = flags & ~(SOCK_CREATE_KERN | SOCK_CREATE_USER |
SOCK_CREATE_LITE);
+
+ if (type_bits == 0 || (type_bits & (type_bits - 1)) != 0)
+ return -EINVAL;
+
+ if (optional_flags & ~(SOCK_CREATE_NOLSM | SOCK_CREATE_NOWARN))
+ return -EINVAL;
+
+ switch (type_bits) {
+ case SOCK_CREATE_KERN: return __sock_create_flags(net, family, type, protocol,
+ res, 1, optional_flags);
+ case SOCK_CREATE_USER: return __sock_create_flags(current->nsproxy->net_ns,
+ family, type, protocol, res, 0, optional_flags);
+ case SOCK_CREATE_LITE: return __sock_create_lite_flags(family,
+ type, protocol, res, optional_flags);
+ default: return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(sock_open);
+
/**
* sock_create_kern - creates a socket (kernel space)
* @net: net namespace
--
2.47.3
^ permalink raw reply related
* Re: [PATCH net v4] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
From: Petr Wozniak @ 2026-06-21 10:06 UTC (permalink / raw)
To: netdev, sd
Cc: steffen.klassert, herbert, kuba, horms, pabeni, edumazet, davem
In-Reply-To: <20260603064659.3867-1-petr.wozniak@gmail.com>
Reposting on the list, as you asked.
Apologies for missing your comment about the 2nd x->type_offload->xmit()
call across several versions -- entirely my fault.
You're right: in the skb_list_walk_safe() loop, if all GSO segments
return -EINPROGRESS, skb is advanced to NULL and the function returns
NULL instead of ERR_PTR(-EINPROGRESS). v5 1/2 fixes it:
- return skb;
+ return skb ? skb : ERR_PTR(-EINPROGRESS);
At that point NULL can only mean all segments were stolen -- the error
path (err != -EINPROGRESS) returns NULL directly from inside the loop.
v5 1/2 also restores the blank line in validate_xmit_skb_list() and adds
the missing maintainers to Cc.
For the use-after-free I mentioned: I confirmed it. validate_xmit_xfrm()
unlinks async-stolen segments but never updates the list head ->prev, so
when the last segment is stolen, validate_xmit_skb_list() chains onto it
via tail->next. v5 2/2 fixes it by repointing skb->prev at the last
retained segment. As you suggested, the two fixes go as a series.
I could not confirm the head-list leak on a closer look, so I left it
out; I'll send a separate patch if I find it.
The v5 series has been sent.
Thanks,
Petr
^ permalink raw reply
* [PATCH net v5 2/2] xfrm: fix stale skb->prev after async crypto steals a GSO segment
From: Petr Wozniak @ 2026-06-21 10:03 UTC (permalink / raw)
To: netdev
Cc: sd, steffen.klassert, herbert, kuba, horms, pabeni, edumazet,
davem, Petr Wozniak
In-Reply-To: <20260621100327.40203-1-petr.wozniak@gmail.com>
skb_gso_segment() leaves the segment list head with ->prev pointing at
the last segment, an invariant validate_xmit_skb_list() relies on when
it sets its tail pointer (tail = skb->prev).
When validate_xmit_xfrm() walks a GSO list and some segments are stolen
by async crypto (->xmit() returns -EINPROGRESS), those segments are
unlinked from the list but the head ->prev is never updated. If the
last segment is the one stolen, the returned head still has ->prev
pointing at it, even though it is now owned by the crypto engine and may
be freed. validate_xmit_skb_list() later does tail->next = skb, writing
through that stale pointer -- a use-after-free.
Repoint skb->prev at the last retained segment before returning.
Fixes: f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.")
Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com>
---
net/xfrm/xfrm_device.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 19c77f09acc9..aec1e1184a71 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -224,6 +224,14 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
pskb = skb2;
}
+ /* skb_gso_segment() set skb->prev to the last segment, but async
+ * crypto may have stolen it above without updating ->prev. Repoint
+ * it at the last retained segment so validate_xmit_skb_list() does
+ * not chain onto a segment now owned by the crypto engine.
+ */
+ if (skb)
+ skb->prev = pskb;
+
return skb ? skb : ERR_PTR(-EINPROGRESS);
}
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
--
2.51.0
^ permalink raw reply related
* [PATCH net v5 1/2] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
From: Petr Wozniak @ 2026-06-21 10:03 UTC (permalink / raw)
To: netdev
Cc: sd, steffen.klassert, herbert, kuba, horms, pabeni, edumazet,
davem, Petr Wozniak
In-Reply-To: <20260621100327.40203-1-petr.wozniak@gmail.com>
validate_xmit_xfrm() returns NULL both when a packet is dropped and
when it is stolen by async crypto (-EINPROGRESS from ->xmit()).
Callers cannot distinguish the two cases.
f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.")
changed the semantics of a NULL return from "dropped" to "stolen or
dropped", but __dev_queue_xmit() was not updated. On virtual/bridge
interfaces (noqueue qdisc) __dev_queue_xmit() initialises rc=-ENOMEM
and jumps to out: when skb is NULL, returning -ENOMEM to the caller
even though the packet will be delivered correctly via xfrm_dev_resume().
Return ERR_PTR(-EINPROGRESS) from validate_xmit_xfrm() for the async
case so callers can tell it apart from a real drop. Update
__dev_queue_xmit() to handle ERR_PTR(-EINPROGRESS) from
validate_xmit_skb() correctly. Update validate_xmit_skb_list() to
use IS_ERR_OR_NULL() so that ERR_PTR(-EINPROGRESS) is not mistakenly
added to the transmitted list.
Fixes: f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.")
Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com>
---
net/core/dev.c | 10 ++++++++--
net/xfrm/xfrm_device.c | 4 ++--
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 5c01dfaa6c44..f7ffc4d29597 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4018,6 +4018,9 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb,
return NULL;
}
+/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS)
+ * if stolen by async xfrm crypto (delivered via xfrm_dev_resume()).
+ */
static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again)
{
netdev_features_t features;
@@ -4089,7 +4092,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
skb->prev = skb;
skb = validate_xmit_skb(skb, dev, again);
- if (!skb)
+ if (IS_ERR_OR_NULL(skb))
continue;
if (!head)
@@ -4860,8 +4863,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
goto recursion_alert;
skb = validate_xmit_skb(skb, dev, &again);
- if (!skb)
+ if (IS_ERR_OR_NULL(skb)) {
+ if (PTR_ERR(skb) == -EINPROGRESS)
+ rc = NET_XMIT_SUCCESS;
goto out;
+ }
HARD_TX_LOCK(dev, txq, cpu);
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 630f3dd31cc5..19c77f09acc9 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
err = x->type_offload->xmit(x, skb, esp_features);
if (err) {
if (err == -EINPROGRESS)
- return NULL;
+ return ERR_PTR(-EINPROGRESS);
XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);
kfree_skb(skb);
@@ -224,7 +224,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
pskb = skb2;
}
- return skb;
+ return skb ? skb : ERR_PTR(-EINPROGRESS);
}
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
--
2.51.0
^ permalink raw reply related
* [PATCH net v5 0/2] xfrm: fix async crypto (-EINPROGRESS) handling in validate_xmit_xfrm()
From: Petr Wozniak @ 2026-06-21 10:03 UTC (permalink / raw)
To: netdev
Cc: sd, steffen.klassert, herbert, kuba, horms, pabeni, edumazet,
davem, Petr Wozniak
In-Reply-To: <20260603064659.3867-1-petr.wozniak@gmail.com>
This series fixes how the async crypto path (-EINPROGRESS from ->xmit())
is handled in validate_xmit_xfrm() and its callers.
Patch 1 (previously sent on its own, v1-v4) makes validate_xmit_xfrm()
return ERR_PTR(-EINPROGRESS) instead of NULL when a packet is stolen by
async crypto, so __dev_queue_xmit() can tell it apart from a real drop
and stop reporting -ENOMEM on noqueue/bridge interfaces. v5 also covers
the GSO segment loop, as Sabrina pointed out.
Patch 2 fixes a use-after-free found while looking at that GSO loop:
validate_xmit_xfrm() unlinks async-stolen segments but never updates the
list head ->prev, which validate_xmit_skb_list() later dereferences.
Changes in v5:
- 1/2: also propagate ERR_PTR(-EINPROGRESS) from the GSO segment loop
(the 2nd ->xmit() call); v4 only handled the single-skb path. Restore
the blank line in validate_xmit_skb_list(). Add the missing
maintainers to Cc. (Sabrina Dubroca)
- 2/2: new patch -- fix the stale skb->prev use-after-free (also flagged
by Sashiko)
Changes in v4:
- Drop bool stolen tracking and the ERR_PTR return in
validate_xmit_skb_list(); use IS_ERR_OR_NULL() so stolen skbs are
silently skipped (Sabrina Dubroca)
- Drop ERR_PTR(-EINPROGRESS) handling in __dev_direct_xmit() (Sabrina Dubroca)
- Move validate_xmit_skb() return-value comment above the function
(Sabrina Dubroca)
Changes in v3:
- validate_xmit_skb_list(): set stolen=true only for -EINPROGRESS
(Sabrina Dubroca)
Changes in v2:
- Reset rc to NET_XMIT_SUCCESS only when PTR_ERR(skb) == -EINPROGRESS
(Sabrina Dubroca)
Petr Wozniak (2):
xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
xfrm: fix stale skb->prev after async crypto steals a GSO segment
net/core/dev.c | 10 ++++++++--
net/xfrm/xfrm_device.c | 12 ++++++++++--
2 files changed, 18 insertions(+), 4 deletions(-)
--
2.51.0
^ permalink raw reply
* Re: [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures
From: Andrew Lunn @ 2026-06-21 9:06 UTC (permalink / raw)
To: David Yang
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
In-Reply-To: <CAAXyoMNOwiQCta9CKVtoBUwmX5JL-1MaDjxLrbWyviADyfs=sQ@mail.gmail.com>
> > This seems pretty error prone. A missing check will result in an
> > opps. At least it will be obvious. How big is each port structure? Is
> > the memory saving worth it?
> >
> > Andrew
>
> It's about 1.4k per port for 5 dummy ports.
That adds up, so not allocating them makes sense.
Maybe check if the functions you are adding tests to can actually be
called for dummy ports. They should not have a netdev, so that often
means there is no path to call these functions.
Andrew
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-next v2] ixgbe: Implement PCI reset handler
From: Temerkhanov, Sergey @ 2026-06-21 7:52 UTC (permalink / raw)
To: Temerkhanov, Sergey, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, pmenzel@molgen.mpg.de
In-Reply-To: <20260618142212.310475-1-sergey.temerkhanov@intel.com>
Please disregard, this is a broken version mistakenly sent.
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of
> Sergey Temerkhanov
> Sent: Thursday, June 18, 2026 4:22 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org; pmenzel@molgen.mpg.de
> Subject: [Intel-wired-lan] [PATCH iwl-next v2] ixgbe: Implement PCI reset
> handler
>
> Implement PCI device reset handler to allow the network device to get re-
> initialized and function after a PCI-level reset.
>
> This is necessary for the adapter to avoid TX queue timeouts occurring when
> the PCI reset is initiated via sysfs during the operation
>
> Signed-off-by: Sergey Temerkhanov <sergey.temerkhanov@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
>
> Previous version:
> https://lore.kernel.org/netdev/MW4PR11MB6864BC9CA84F060AF7E02484
> 80E42@MW4PR11MB6864.namprd11.prod.outlook.com/
> v1->v2 changes: Rearranged the order of operations, switched to
> v1->poll_timeout_us() macro
>
> drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 +
> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 82 +++++++++++++++++++
> 2 files changed, 83 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
> b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
> index 594ccb28da20..c4b0c5bb89c6 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
> @@ -912,6 +912,7 @@ enum ixgbe_state_t {
> __IXGBE_PTP_TX_IN_PROGRESS,
> __IXGBE_RESET_REQUESTED,
> __IXGBE_PHY_INIT_COMPLETE,
> + __IXGBE_PCIE_RESET_IN_PROGRESS,
> };
>
> struct ixgbe_cb {
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> index 2ac274c73d61..0fb64aef223e 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> @@ -12352,6 +12352,86 @@ static pci_ers_result_t
> ixgbe_io_slot_reset(struct pci_dev *pdev)
> return result;
> }
>
> +/* 1500 us poll interval */
> +#define IXGBE_RESET_PREP_POLL_INTERVAL_US 1500
> +/* 2 second timeout to acquire reset lock before proceeding */ #define
> +IXGBE_RESET_PREP_TIMEOUT_US 2000000
> +
> +/**
> + * ixgbe_reset_prep - called before the pci bus is reset.
> + * @pdev: Pointer to PCI device
> + *
> + * Prepare the card for a reset, preventing the service task from running.
> + */
> +static void ixgbe_reset_prep(struct pci_dev *pdev) {
> + struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
> +
> + if (!adapter)
> + return;
> +
> + if (poll_timeout_us(test_and_set_bit(__IXGBE_RESETTING, &adapter-
> >state),
> + test_bit(__IXGBE_RESETTING, &adapter->state),
> + IXGBE_RESET_PREP_POLL_INTERVAL_US,
> + IXGBE_RESET_PREP_TIMEOUT_US, false)) {
> + /* ixgbe_reset_done() will exit early if this happens.
> + * A retry will be needed
> + */
> + e_err(drv, "Timed out waiting for __IXGBE_RESETTING to be
> released. Reset is needed\n");
> + return;
> + }
> +
> + /* Sync __IXGBE_RESETTING */
> + smp_mb__after_atomic();
> +
> + if (test_bit(__IXGBE_SERVICE_INITED, &adapter->state)) {
> + /* Prevent the service task from being requeued in the timer
> callback */
> + timer_delete_sync(&adapter->service_timer);
> + /* Cancel any possibly queued service task */
> + cancel_work_sync(&adapter->service_task);
> + }
> +
> + pci_clear_master(pdev);
> +
> + set_bit(__IXGBE_PCIE_RESET_IN_PROGRESS, &adapter->state); }
> +
> +/**
> + * ixgbe_reset_done - called after the pci bus has been reset.
> + * @pdev: Pointer to PCI device
> + *
> + * Allow the service task to run and schedule re-initialization.
> + */
> +static void ixgbe_reset_done(struct pci_dev *pdev) {
> + struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
> +
> + if (!adapter)
> + return;
> +
> + if (!test_and_clear_bit(__IXGBE_PCIE_RESET_IN_PROGRESS,
> &adapter->state)) {
> + /* Should never get here */
> + e_err(drv, "Reset done called without PCIe reset in
> progress\n");
> + return;
> + }
> +
> + pci_set_master(pdev);
> +
> + /* Allow the service task to run */
> + if (!test_bit(__IXGBE_REMOVING, &adapter->state)) {
> + clear_bit(__IXGBE_RESETTING, &adapter->state);
> + /* Sync __IXGBE_RESETTING */
> + smp_mb__after_atomic();
> + }
> +
> + /* Schedule re-initialization */
> + if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
> + set_bit(__IXGBE_RESET_REQUESTED, &adapter->state);
> + if (test_bit(__IXGBE_SERVICE_INITED, &adapter->state))
> + mod_timer(&adapter->service_timer, jiffies + 1);
> + }
> +}
> +
> /**
> * ixgbe_io_resume - called when traffic can start flowing again.
> * @pdev: Pointer to PCI device
> @@ -12384,6 +12464,8 @@ static const struct pci_error_handlers
> ixgbe_err_handler = {
> .error_detected = ixgbe_io_error_detected,
> .slot_reset = ixgbe_io_slot_reset,
> .resume = ixgbe_io_resume,
> + .reset_prepare = ixgbe_reset_prep,
> + .reset_done = ixgbe_reset_done,
> };
>
> static DEFINE_SIMPLE_DEV_PM_OPS(ixgbe_pm_ops, ixgbe_suspend,
> ixgbe_resume);
> --
> 2.53.0
^ permalink raw reply
* [syzbot] [nbd?] WARNING in nbd_add_socket
From: syzbot @ 2026-06-21 6:23 UTC (permalink / raw)
To: axboe, josef, linux-block, linux-kernel, nbd, netdev,
syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: b85966adbf5d Merge tag 'net-next-7.2' of git://git.kernel...
git tree: net
console output: https://syzkaller.appspot.com/x/log.txt?x=101f6d56580000
kernel config: https://syzkaller.appspot.com/x/.config?x=9a9f723a32776544
dashboard link: https://syzkaller.appspot.com/bug?extid=6b85d1e39a5b8ed9a954
compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=13584aae580000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=11fd7b7a580000
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/780edcc3cc37/disk-b85966ad.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/967dd18c7ecd/vmlinux-b85966ad.xz
kernel image: https://storage.googleapis.com/syzbot-assets/cf9fa92c90ff/bzImage-b85966ad.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+6b85d1e39a5b8ed9a954@syzkaller.appspotmail.com
netlink: 3936 bytes leftover after parsing attributes in process `syz.0.25'.
------------[ cut here ]------------
!sock_allow_reclassification(sk)
WARNING: drivers/block/nbd.c:1249 at nbd_reclassify_socket drivers/block/nbd.c:1249 [inline], CPU#0: syz.0.25/5992
WARNING: drivers/block/nbd.c:1249 at nbd_add_socket+0xf35/0x12c0 drivers/block/nbd.c:1293, CPU#0: syz.0.25/5992
Modules linked in:
CPU: 0 UID: 0 PID: 5992 Comm: syz.0.25 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
RIP: 0010:nbd_reclassify_socket drivers/block/nbd.c:1249 [inline]
RIP: 0010:nbd_add_socket+0xf35/0x12c0 drivers/block/nbd.c:1293
Code: f7 e8 6f b5 20 fc bf e0 01 00 00 49 03 3e 48 c7 c6 40 02 55 8c e8 2b a8 1b fb b8 f0 ff ff ff e9 b2 fd ff ff e8 ac 60 b5 fb 90 <0f> 0b 90 e9 16 f8 ff ff e8 5e 2e 97 05 44 89 e9 80 e1 07 fe c1 38
RSP: 0018:ffffc90002ef7160 EFLAGS: 00010293
RAX: ffffffff86109574 RBX: 1ffff1100651ddb9 RCX: ffff888020b68000
RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000000000000
RBP: ffffc90002ef7250 R08: ffff888035af2bdf R09: 1ffff11006b5e57b
R10: dffffc0000000000 R11: ffffed1006b5e57c R12: ffff8880328eec00
R13: 1ffff920005dee38 R14: dffffc0000000000 R15: 0000000000000001
FS: 00007fcc9d5dd6c0(0000) GS:ffff88812527c000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f81a8ab50f0 CR3: 0000000078780000 CR4: 00000000003526f0
Call Trace:
<TASK>
nbd_genl_connect+0x133d/0x1c10 drivers/block/nbd.c:2254
genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x7bb/0x940 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec net/socket.c:775 [inline]
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x9b9/0xa20 net/socket.c:2684
___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2773
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fcc9df9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fcc9d5dd028 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007fcc9e216090 RCX: 00007fcc9df9ce59
RDX: 0000000000004040 RSI: 0000200000000140 RDI: 0000000000000004
RBP: 00007fcc9e032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fcc9e216128 R14: 00007fcc9e216090 R15: 00007ffc8f827678
</TASK>
---
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 syzkaller@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 syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
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
^ permalink raw reply
* Re: [PATCH net] tipc: restrict socket queue dumps in enqueue tracepoints
From: Greg KH @ 2026-06-21 5:39 UTC (permalink / raw)
To: XIAO WU
Cc: Li Xiasong, Jon Maloy, stable, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Ying Xue, Tuong Lien,
netdev, tipc-discussion, yuehaibing, zhangchangzhong, weiyongjun1
In-Reply-To: <tencent_EC8B2032C1F9358EA3B49645F0F2277B210A@qq.com>
On Sun, Jun 21, 2026 at 09:21:15AM +0800, XIAO WU wrote:
> Hi Li Xiasong,
>
> I see this patch was merged into net.git as commit acd7df8d9554 — thanks
> for the fix. However, a Sashiko AI code review [1] flagged that
> `tipc_poll()` in the same file has the identical pre-existing issue: it
> calls `trace_tipc_sk_poll()` with `TIPC_DUMP_ALL`, which triggers a dump
> of all socket queues without holding the socket owner lock. The merged
> fix addressed `tipc_sk_enqueue()` but left `tipc_poll()` unchanged.
>
> I was able to reproduce the remaining use-after-free in QEMU with KASAN
> by racing `tipc_poll()` against `tipc_recvmsg()` on the same socket.
Great, can you send a fix for this?
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH net v2 0/2] net: ethernet: sunplus: spl2sw: fix of_node refcount leaks
From: 呂芳騰 @ 2026-06-21 4:38 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Shitalkumar Gandhi, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, netdev, linux-kernel,
Shitalkumar Gandhi
In-Reply-To: <20260618175619.671a4025@kernel.org>
Hi Jakub,
I'm sorry that I can't test the fix.
I've left from Suplus and don't have the relevant hardware.
Best regards,
Wells Lu
Jakub Kicinski <kuba@kernel.org> 於 2026年6月19日週五 上午8:56寫道:
>
> On Tue, 16 Jun 2026 01:20:30 +0530 Shitalkumar Gandhi wrote:
> > This series fixes of_node refcount leaks in the Sunplus SP7021 ethernet
> > driver, found by inspection. Compile-tested only; no SP7021 hardware
> > available here.
> >
> > Patch 1/2 fixes the phy_node leak in the remove path.
> > Patch 2/2 fixes multiple leaks in the probe path and depends on the
> > cleanup contract from patch 1/2.
>
> Wells Lu, please review.
> --
> mping: SUNPLUS ETHERNET DRIVER
^ permalink raw reply
* [PATCH] net: wwan: t7xx: destroy DMA pool on CLDMA late init failure
From: Haoxiang Li @ 2026-06-21 3:17 UTC (permalink / raw)
To: chandrashekar.devegowda, haijun.liu, ricardo.martinez,
loic.poulain, ryazanov.s.a, johannes, andrew+netdev, davem,
edumazet, kuba, pabeni, ilpo.jarvinen
Cc: netdev, linux-kernel, Haoxiang Li, stable
t7xx_cldma_late_init() creates md_ctrl->gpd_dmapool before
initializing the TX and RX rings. If any ring initialization
fails, the error path frees the already initialized rings but
leaves the DMA pool allocated.
Destroy md_ctrl->gpd_dmapool on the late-init failure path
to avoid leaking the DMA pool.
Fixes: 39d439047f1d ("net: wwan: t7xx: Add control DMA interface")
Cc: stable@vger.kernel.org
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
---
drivers/net/wwan/t7xx/t7xx_hif_cldma.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
index e10cb4f9104e..2917cee9b802 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
@@ -1063,6 +1063,9 @@ static int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl)
while (i--)
t7xx_cldma_ring_free(md_ctrl, &md_ctrl->tx_ring[i], DMA_TO_DEVICE);
+ dma_pool_destroy(md_ctrl->gpd_dmapool);
+ md_ctrl->gpd_dmapool = NULL;
+
return ret;
}
--
2.25.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox