Follow-up to commit 120977e2c096 ("net/sched: cls_bpf: reject dev-bound programs bound to a different device"), which derived the target device from block->q: dev = block->q ? qdisc_dev(block->q) : NULL; tcf_block_create() deliberately leaves block->q NULL for shared blocks, so a device-bound program attached through a shared block is rejected with -EINVAL even when it is bound to a netdev that owns the block. The offload path (cls_bpf_offload_cmd()) never consults block->q and drives the per-device callbacks for shared blocks as before. Accept the attach on a shared block when bpf_offload_dev_match() matches every netdev bound to the block, and reject an empty block. Every member must match because the offload path broadcasts the program to every callback registered on the block, so an any-member check would re-admit the wrong-device attach on a mixed block. Conditions to recreate the bug: - CONFIG_NETDEVSIM=y, CONFIG_NET_CLS_BPF=y - load a dev-bound SCHED_CLS program for netdevsim device B (prog_ifindex=B), pin it in bpffs - tc qdisc add dev B ingress_block 22 clsact - tc filter add block 22 ingress bpf da object-pinned skip_sw Unfixed, the filter add fails with -EINVAL ("Program is bound to a different device"); fixed, it succeeds. Adding a second, independently backed netdevsim device to the same block makes the attach fail again. Fixes: 120977e2c096deea4e866e4273be9220b957c29e ("net/sched: cls_bpf: reject dev-bound programs bound to a different device") Reported-by: Sashiko (nipa) Closes: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260809094418.901607-1-jhs@mojatatu.com Link: https://lore.kernel.org/netdev/20260809094418.901607-1-jhs@mojatatu.com/ Reviewed-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim --- net/sched/cls_bpf.c | 26 ++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 188cf0f949dd..fe3bf251a39b 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -392,9 +392,29 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, if (bpf_prog_is_dev_bound(fp->aux)) { struct tcf_block *block = tp->chain->block; struct net_device *dev; - - dev = block->q ? qdisc_dev(block->q) : NULL; - if (!dev || !bpf_offload_dev_match(fp, dev)) { + unsigned long ifindex; + bool found = false; + bool match = false; + + /* A shared block has no qdisc (block->q == NULL) but may + * bind several netdevs; the program is offloaded to all of + * them, so it must match all of them. + */ + if (!tcf_block_shared(block)) { + match = bpf_offload_dev_match(fp, qdisc_dev(tcf_block_q(block))); + } else { + xa_for_each(&block->ports, ifindex, dev) { + found = true; + if (!bpf_offload_dev_match(fp, dev)) { + match = false; + break; + } + match = true; + } + if (!found) + match = false; + } + if (!match) { NL_SET_ERR_MSG(extack, "Program is bound to a different device"); bpf_prog_put(fp); -- 2.43.0