[PATCH] [RFC PATCH] bpf/xdp: propagate queue_index via xdp_frame for cpumap

Adith-Joshua posted 1 patch 2 months, 2 weeks ago
include/net/xdp.h     |  4 ++++
kernel/bpf/cpumap.c   |  2 +-
kernel/bpf/verifier.c | 12 ++++++++----
net/core/xdp.c        |  1 +
4 files changed, 14 insertions(+), 5 deletions(-)
[PATCH] [RFC PATCH] bpf/xdp: propagate queue_index via xdp_frame for cpumap
Posted by Adith-Joshua 2 months, 2 weeks ago
cpu_map_bpf_prog_run_xdp() currently lacks queue_index information in
xdp_rxq_info, as struct xdp_frame does not preserve it across the
xdp_buff -> xdp_frame conversion.

This results in loss of rx queue metadata when frames are redirected
via cpumap and later processed by XDP programs.

Propagate queue_index by storing it in struct xdp_frame during
conversion and restoring it when reconstructing xdp_buff. All
relevant xdp_frame constructors have been updated to ensure the field is consistently
initialized.

This introduces a new field in struct xdp_frame, which may have
implications for structure size and layout. Field reordering was
considered to minimize padding, but is not included here to keep the
change focused and avoid unrelated modifications.

Feedback is appreciated on whether this approach is acceptable or if a
different mechanism or struct layout would be preferred.

Signed-off-by: Adith-Joshua <adithalex29@gmail.com>
---
 include/net/xdp.h     |  4 ++++
 kernel/bpf/cpumap.c   |  2 +-
 kernel/bpf/verifier.c | 12 ++++++++----
 net/core/xdp.c        |  1 +
 4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/include/net/xdp.h b/include/net/xdp.h
index aa742f413c35..11aecf84a896 100644
--- a/include/net/xdp.h
+++ b/include/net/xdp.h
@@ -301,6 +301,7 @@ struct xdp_frame {
 	 */
 	enum xdp_mem_type mem_type:32;
 	struct net_device *dev_rx; /* used by cpumap */
+	u32 queue_index;
 	u32 frame_sz;
 	u32 flags; /* supported values defined in xdp_buff_flags */
 };
@@ -347,6 +348,7 @@ static inline void xdp_scrub_frame(struct xdp_frame *frame)
 {
 	frame->data = NULL;
 	frame->dev_rx = NULL;
+	frame->queue_index = 0;
 }
 
 static inline void
@@ -392,6 +394,7 @@ void xdp_convert_frame_to_buff(const struct xdp_frame *frame,
 	xdp->data = frame->data;
 	xdp->data_end = frame->data + frame->len;
 	xdp->data_meta = frame->data - frame->metasize;
+	xdp->rxq->queue_index = frame->queue_index;
 	xdp->frame_sz = frame->frame_sz;
 	xdp->flags = frame->flags;
 }
@@ -419,6 +422,7 @@ int xdp_update_frame_from_buff(const struct xdp_buff *xdp,
 	xdp_frame->len  = xdp->data_end - xdp->data;
 	xdp_frame->headroom = headroom - sizeof(*xdp_frame);
 	xdp_frame->metasize = metasize;
+	xdp_frame->queue_index = xdp->rxq->queue_index;
 	xdp_frame->frame_sz = xdp->frame_sz;
 	xdp_frame->flags = xdp->flags;
 
diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c
index 32b43cb9061b..e475a732ceca 100644
--- a/kernel/bpf/cpumap.c
+++ b/kernel/bpf/cpumap.c
@@ -197,7 +197,7 @@ static int cpu_map_bpf_prog_run_xdp(struct bpf_cpu_map_entry *rcpu,
 
 		rxq.dev = xdpf->dev_rx;
 		rxq.mem.type = xdpf->mem_type;
-		/* TODO: report queue_index to xdp_rxq_info */
+		rxq.queue_index = xdpf->queue_index;
 
 		xdp_convert_frame_to_buff(xdpf, &xdp);
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e3814152b52f..e2583dfd7bf2 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -18693,12 +18693,16 @@ static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,
 	int i;
 
 	for (i = 0; i < env->insn_array_map_cnt; i++) {
-		/*
-		 * TODO (when needed): collect only jump tables, not static keys
-		 * or maps for indirect calls
-		 */
 		map = env->insn_array_maps[i];
 
+		/* Only consider instruction array maps with multiple entries.
+		 * These correspond to jump tables. Skip others (e.g. static keys,
+		 * indirect call maps).
+		 */
+		if (map->map_type != BPF_MAP_TYPE_INSN_ARRAY ||
+		    map->max_entries <= 1)
+			continue;
+
 		jt_cur = jt_from_map(map);
 		if (IS_ERR(jt_cur)) {
 			kvfree(jt);
diff --git a/net/core/xdp.c b/net/core/xdp.c
index 9890a30584ba..995f48e35310 100644
--- a/net/core/xdp.c
+++ b/net/core/xdp.c
@@ -604,6 +604,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_frame(struct xdp_buff *xdp)
 	xdpf->len = totsize - metasize;
 	xdpf->headroom = 0;
 	xdpf->metasize = metasize;
+	xdpf->queue_index = xdp->rxq->queue_index;
 	xdpf->frame_sz = PAGE_SIZE;
 	xdpf->mem_type = MEM_TYPE_PAGE_ORDER0;
 
-- 
2.53.0
Re: [PATCH] [RFC PATCH] bpf/xdp: propagate queue_index via xdp_frame for cpumap
Posted by bot+bpf-ci@kernel.org 2 months, 2 weeks ago
> diff --git a/include/net/xdp.h b/include/net/xdp.h
> index aa742f413c35..11aecf84a896 100644
> --- a/include/net/xdp.h
> +++ b/include/net/xdp.h

[ ... ]

> @@ -392,6 +394,7 @@ void xdp_convert_frame_to_buff(const struct xdp_frame *frame,
>  	xdp->data = frame->data;
>  	xdp->data_end = frame->data + frame->len;
>  	xdp->data_meta = frame->data - frame->metasize;
> +	xdp->rxq->queue_index = frame->queue_index;
>  	xdp->frame_sz = frame->frame_sz;
>  	xdp->flags = frame->flags;
>  }

This new line dereferences xdp->rxq, but several callers of
xdp_convert_frame_to_buff() do not set xdp->rxq before calling it.

In veth_xdp_rcv_one():

    struct veth_xdp_buff vxbuf;
    struct xdp_buff *xdp = &vxbuf.xdp;

    xdp_convert_frame_to_buff(frame, xdp);   /* rxq is garbage here */
    xdp->rxq = &rq->xdp_rxq;                /* set too late */

In dev_map_bpf_prog_run():

    struct xdp_buff xdp;

    xdp_convert_frame_to_buff(xdpf, &xdp);   /* rxq is garbage here */
    xdp.txq = &txq;
    xdp.rxq = &rxq;                          /* set too late */

In bond_xdp_xmit():

    struct xdp_buff xdp;

    xdp_convert_frame_to_buff(frame, &xdp);   /* rxq is garbage here */
    /* rxq is never set */

In all three cases, xdp_buff is stack-allocated without initialization,
so xdp->rxq contains whatever was on the stack.  Writing to
xdp->rxq->queue_index then corrupts an arbitrary kernel address.

Would it be better to move the queue_index restore out of
xdp_convert_frame_to_buff() and into the callers that actually need it
(i.e. cpu_map_bpf_prog_run_xdp), or alternatively update all callers
to set xdp->rxq before calling this function?

> @@ -419,6 +422,7 @@ int xdp_update_frame_from_buff(const struct xdp_buff *xdp,
>  	xdp_frame->len  = xdp->data_end - xdp->data;
>  	xdp_frame->headroom = headroom - sizeof(*xdp_frame);
>  	xdp_frame->metasize = metasize;
> +	xdp_frame->queue_index = xdp->rxq->queue_index;
>  	xdp_frame->frame_sz = xdp->frame_sz;
>  	xdp_frame->flags = xdp->flags;

Same concern here: xdp_update_frame_from_buff() now also dereferences
xdp->rxq.  While callers that reach this point after surviving the
xdp_convert_frame_to_buff() crash would have rxq set by then, this
creates a hidden precondition that xdp->rxq must be valid, which was
not previously required by either function.

> diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c
> index 5e59ab896f05..448da572de9a 100644
> --- a/kernel/bpf/cpumap.c
> +++ b/kernel/bpf/cpumap.c
> @@ -197,7 +197,7 @@ static int cpu_map_bpf_prog_run_xdp(struct bpf_cpu_map_entry *rcpu,
>
>  		rxq.dev = xdpf->dev_rx;
>  		rxq.mem.type = xdpf->mem_type;
> -		/* TODO: report queue_index to xdp_rxq_info */
> +		rxq.queue_index = xdpf->queue_index;
>
>  		xdp_convert_frame_to_buff(xdpf, &xdp);

This is the one caller where xdp.rxq is set before calling
xdp_convert_frame_to_buff(), so it works correctly here.  However, the
explicit rxq.queue_index assignment above is now redundant with the
assignment inside xdp_convert_frame_to_buff().

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 84699a428077..83566d2f446e 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -19098,12 +19098,16 @@ static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,
>  	int i;
>
>  	for (i = 0; i < env->insn_array_map_cnt; i++) {
> -		/*
> -		 * TODO (when needed): collect only jump tables, not static keys
> -		 * or maps for indirect calls
> -		 */
>  		map = env->insn_array_maps[i];
>
> +		/* Only consider instruction array maps with multiple entries.
> +		 * These correspond to jump tables. Skip others (e.g. static keys,
> +		 * indirect call maps).
> +		 */
> +		if (map->map_type != BPF_MAP_TYPE_INSN_ARRAY ||
> +		    map->max_entries <= 1)
> +			continue;
> +

This change to jt_from_subprog() appears unrelated to the xdp
queue_index propagation described in the commit message.  Was this
intended to be part of this patch, or should it be a separate commit?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/24042390210
Re: [PATCH] [RFC PATCH] bpf/xdp: propagate queue_index via xdp_frame for cpumap
Posted by Alexei Starovoitov 2 months, 2 weeks ago
On Mon, Apr 6, 2026 at 10:12 AM Adith-Joshua <adithalex29@gmail.com> wrote:
>
> cpu_map_bpf_prog_run_xdp() currently lacks queue_index information in
> xdp_rxq_info, as struct xdp_frame does not preserve it across the
> xdp_buff -> xdp_frame conversion.
>
> This results in loss of rx queue metadata when frames are redirected
> via cpumap and later processed by XDP programs.
>
> Propagate queue_index by storing it in struct xdp_frame during
> conversion and restoring it when reconstructing xdp_buff. All
> relevant xdp_frame constructors have been updated to ensure the field is consistently
> initialized.
>
> This introduces a new field in struct xdp_frame, which may have
> implications for structure size and layout. Field reordering was
> considered to minimize padding, but is not included here to keep the
> change focused and avoid unrelated modifications.
>
> Feedback is appreciated on whether this approach is acceptable or if a
> different mechanism or struct layout would be preferred.
>
> Signed-off-by: Adith-Joshua <adithalex29@gmail.com>
> ---
>  include/net/xdp.h     |  4 ++++
>  kernel/bpf/cpumap.c   |  2 +-
>  kernel/bpf/verifier.c | 12 ++++++++----
>  net/core/xdp.c        |  1 +
>  4 files changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/xdp.h b/include/net/xdp.h
> index aa742f413c35..11aecf84a896 100644
> --- a/include/net/xdp.h
> +++ b/include/net/xdp.h
> @@ -301,6 +301,7 @@ struct xdp_frame {
>          */
>         enum xdp_mem_type mem_type:32;
>         struct net_device *dev_rx; /* used by cpumap */
> +       u32 queue_index;
>         u32 frame_sz;
>         u32 flags; /* supported values defined in xdp_buff_flags */
>  };
> @@ -347,6 +348,7 @@ static inline void xdp_scrub_frame(struct xdp_frame *frame)
>  {
>         frame->data = NULL;
>         frame->dev_rx = NULL;
> +       frame->queue_index = 0;
>  }
>
>  static inline void
> @@ -392,6 +394,7 @@ void xdp_convert_frame_to_buff(const struct xdp_frame *frame,
>         xdp->data = frame->data;
>         xdp->data_end = frame->data + frame->len;
>         xdp->data_meta = frame->data - frame->metasize;
> +       xdp->rxq->queue_index = frame->queue_index;
>         xdp->frame_sz = frame->frame_sz;
>         xdp->flags = frame->flags;
>  }
> @@ -419,6 +422,7 @@ int xdp_update_frame_from_buff(const struct xdp_buff *xdp,
>         xdp_frame->len  = xdp->data_end - xdp->data;
>         xdp_frame->headroom = headroom - sizeof(*xdp_frame);
>         xdp_frame->metasize = metasize;
> +       xdp_frame->queue_index = xdp->rxq->queue_index;
>         xdp_frame->frame_sz = xdp->frame_sz;
>         xdp_frame->flags = xdp->flags;
>
> diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c
> index 32b43cb9061b..e475a732ceca 100644
> --- a/kernel/bpf/cpumap.c
> +++ b/kernel/bpf/cpumap.c
> @@ -197,7 +197,7 @@ static int cpu_map_bpf_prog_run_xdp(struct bpf_cpu_map_entry *rcpu,
>
>                 rxq.dev = xdpf->dev_rx;
>                 rxq.mem.type = xdpf->mem_type;
> -               /* TODO: report queue_index to xdp_rxq_info */
> +               rxq.queue_index = xdpf->queue_index;
>

nack. please do your homework and research the mailing list first.
Other people already attempted removing this.

pw-bot: cr