From 69050f8d6d075dc01af7a5f2f550a8067510366f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 20 Feb 2026 23:49:23 -0800 Subject: treewide: Replace kmalloc with kmalloc_obj for non-scalar types This is the result of running the Coccinelle script from scripts/coccinelle/api/kmalloc_objs.cocci. The script is designed to avoid scalar types (which need careful case-by-case checking), and instead replace kmalloc-family calls that allocate struct or union object instances: Single allocations: kmalloc(sizeof(TYPE), ...) are replaced with: kmalloc_obj(TYPE, ...) Array allocations: kmalloc_array(COUNT, sizeof(TYPE), ...) are replaced with: kmalloc_objs(TYPE, COUNT, ...) Flex array allocations: kmalloc(struct_size(PTR, FAM, COUNT), ...) are replaced with: kmalloc_flex(*PTR, FAM, COUNT, ...) (where TYPE may also be *VAR) The resulting allocations no longer return "void *", instead returning "TYPE *". Signed-off-by: Kees Cook --- net/core/bpf_sk_storage.c | 2 +- net/core/dev.c | 25 ++++++++++++------------- net/core/devmem.c | 12 ++++++------ net/core/drop_monitor.c | 8 ++++---- net/core/dst.c | 5 ++--- net/core/failover.c | 2 +- net/core/filter.c | 7 +++---- net/core/flow_offload.c | 13 ++++++------- net/core/gen_estimator.c | 2 +- net/core/gro_cells.c | 2 +- net/core/neighbour.c | 2 +- net/core/net_namespace.c | 2 +- net/core/netclassid_cgroup.c | 2 +- net/core/netpoll.c | 2 +- net/core/netprio_cgroup.c | 2 +- net/core/rtnetlink.c | 4 ++-- net/core/scm.c | 2 +- net/core/selftests.c | 2 +- net/core/skmsg.c | 2 +- net/core/sock.c | 2 +- net/core/sock_diag.c | 2 +- net/core/sock_map.c | 2 +- net/core/sock_reuseport.c | 2 +- net/core/xdp.c | 4 ++-- 24 files changed, 53 insertions(+), 57 deletions(-) (limited to 'net/core') diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index 1eb3e060994e..8165d606bfd4 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -500,7 +500,7 @@ bpf_sk_storage_diag_alloc(const struct nlattr *nla_stgs) nr_maps++; } - diag = kzalloc(struct_size(diag, maps, nr_maps), GFP_KERNEL); + diag = kzalloc_flex(*diag, maps, nr_maps, GFP_KERNEL); if (!diag) return ERR_PTR(-ENOMEM); diff --git a/net/core/dev.c b/net/core/dev.c index 096b3ff13f6b..525cf5952101 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -273,7 +273,7 @@ static struct netdev_name_node *netdev_name_node_alloc(struct net_device *dev, { struct netdev_name_node *name_node; - name_node = kmalloc(sizeof(*name_node), GFP_KERNEL); + name_node = kmalloc_obj(*name_node, GFP_KERNEL); if (!name_node) return NULL; INIT_HLIST_NODE(&name_node->hlist); @@ -6510,8 +6510,7 @@ struct flush_backlogs { static struct flush_backlogs *flush_backlogs_alloc(void) { - return kmalloc(struct_size_t(struct flush_backlogs, w, nr_cpu_ids), - GFP_KERNEL); + return kmalloc_flex(struct flush_backlogs, w, nr_cpu_ids, GFP_KERNEL); } static struct flush_backlogs *flush_backlogs_fallback; @@ -8694,7 +8693,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev, return 0; } - adj = kmalloc(sizeof(*adj), GFP_KERNEL); + adj = kmalloc_obj(*adj, GFP_KERNEL); if (!adj) return -ENOMEM; @@ -9134,8 +9133,8 @@ static int netdev_offload_xstats_enable_l3(struct net_device *dev, int err; int rc; - dev->offload_xstats_l3 = kzalloc(sizeof(*dev->offload_xstats_l3), - GFP_KERNEL); + dev->offload_xstats_l3 = kzalloc_obj(*dev->offload_xstats_l3, + GFP_KERNEL); if (!dev->offload_xstats_l3) return -ENOMEM; @@ -10660,7 +10659,7 @@ int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) return -EINVAL; } - link = kzalloc(sizeof(*link), GFP_USER); + link = kzalloc_obj(*link, GFP_USER); if (!link) { err = -ENOMEM; goto unlock; @@ -11941,7 +11940,7 @@ struct netdev_queue *dev_ingress_queue_create(struct net_device *dev) #ifdef CONFIG_NET_CLS_ACT if (queue) return queue; - queue = kzalloc(sizeof(*queue), GFP_KERNEL); + queue = kzalloc_obj(*queue, GFP_KERNEL); if (!queue) return NULL; netdev_init_one_queue(dev, queue, NULL); @@ -12016,8 +12015,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, maxqs = max(txqs, rxqs); - dev = kvzalloc(struct_size(dev, priv, sizeof_priv), - GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL); + dev = kvzalloc_flex(*dev, priv, sizeof_priv, + GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL); if (!dev) return NULL; @@ -12088,11 +12087,11 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev->real_num_rx_queues = rxqs; if (netif_alloc_rx_queues(dev)) goto free_all; - dev->ethtool = kzalloc(sizeof(*dev->ethtool), GFP_KERNEL_ACCOUNT); + dev->ethtool = kzalloc_obj(*dev->ethtool, GFP_KERNEL_ACCOUNT); if (!dev->ethtool) goto free_all; - dev->cfg = kzalloc(sizeof(*dev->cfg), GFP_KERNEL_ACCOUNT); + dev->cfg = kzalloc_obj(*dev->cfg, GFP_KERNEL_ACCOUNT); if (!dev->cfg) goto free_all; dev->cfg_pending = dev->cfg; @@ -12858,7 +12857,7 @@ static struct hlist_head * __net_init netdev_create_hash(void) int i; struct hlist_head *hash; - hash = kmalloc_array(NETDEV_HASHENTRIES, sizeof(*hash), GFP_KERNEL); + hash = kmalloc_objs(*hash, NETDEV_HASHENTRIES, GFP_KERNEL); if (hash != NULL) for (i = 0; i < NETDEV_HASHENTRIES; i++) INIT_HLIST_HEAD(&hash[i]); diff --git a/net/core/devmem.c b/net/core/devmem.c index 63f093f7d2b2..16a1949dbed1 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -241,9 +241,9 @@ net_devmem_bind_dmabuf(struct net_device *dev, } if (direction == DMA_TO_DEVICE) { - binding->tx_vec = kvmalloc_array(dmabuf->size / PAGE_SIZE, - sizeof(struct net_iov *), - GFP_KERNEL); + binding->tx_vec = kvmalloc_objs(struct net_iov *, + dmabuf->size / PAGE_SIZE, + GFP_KERNEL); if (!binding->tx_vec) { err = -ENOMEM; goto err_unmap; @@ -289,9 +289,9 @@ net_devmem_bind_dmabuf(struct net_device *dev, goto err_free_chunks; } - owner->area.niovs = kvmalloc_array(owner->area.num_niovs, - sizeof(*owner->area.niovs), - GFP_KERNEL); + owner->area.niovs = kvmalloc_objs(*owner->area.niovs, + owner->area.num_niovs, + GFP_KERNEL); if (!owner->area.niovs) { err = -ENOMEM; goto err_free_chunks; diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 60d31c2feed3..99f9d4dfb866 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -306,8 +306,8 @@ net_dm_hw_reset_per_cpu_data(struct per_cpu_dm_data *hw_data) struct net_dm_hw_entries *hw_entries; unsigned long flags; - hw_entries = kzalloc(struct_size(hw_entries, entries, dm_hit_limit), - GFP_KERNEL); + hw_entries = kzalloc_flex(*hw_entries, entries, dm_hit_limit, + GFP_KERNEL); if (!hw_entries) { /* If the memory allocation failed, we try to perform another * allocation in 1/10 second. Otherwise, the probe function @@ -856,7 +856,7 @@ net_dm_hw_metadata_copy(const struct devlink_trap_metadata *metadata) const char *trap_group_name; const char *trap_name; - hw_metadata = kzalloc(sizeof(*hw_metadata), GFP_ATOMIC); + hw_metadata = kzalloc_obj(*hw_metadata, GFP_ATOMIC); if (!hw_metadata) return NULL; @@ -1583,7 +1583,7 @@ static int dropmon_net_event(struct notifier_block *ev_block, case NETDEV_REGISTER: if (WARN_ON_ONCE(rtnl_dereference(dev->dm_private))) break; - stat = kzalloc(sizeof(*stat), GFP_KERNEL); + stat = kzalloc_obj(*stat, GFP_KERNEL); if (!stat) break; diff --git a/net/core/dst.c b/net/core/dst.c index 1dae26c51ebe..092861133023 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -191,7 +191,7 @@ EXPORT_SYMBOL(dst_release_immediate); u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old) { - struct dst_metrics *p = kmalloc(sizeof(*p), GFP_ATOMIC); + struct dst_metrics *p = kmalloc_obj(*p, GFP_ATOMIC); if (p) { struct dst_metrics *old_p = (struct dst_metrics *)__DST_METRICS_PTR(old); @@ -295,8 +295,7 @@ struct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type, { struct metadata_dst *md_dst; - md_dst = kmalloc(struct_size(md_dst, u.tun_info.options, optslen), - flags); + md_dst = kmalloc_flex(*md_dst, u.tun_info.options, optslen, flags); if (!md_dst) return NULL; diff --git a/net/core/failover.c b/net/core/failover.c index 2a140b3ea669..fcc04e6995d0 100644 --- a/net/core/failover.c +++ b/net/core/failover.c @@ -247,7 +247,7 @@ struct failover *failover_register(struct net_device *dev, if (dev->type != ARPHRD_ETHER) return ERR_PTR(-EINVAL); - failover = kzalloc(sizeof(*failover), GFP_KERNEL); + failover = kzalloc_obj(*failover, GFP_KERNEL); if (!failover) return ERR_PTR(-ENOMEM); diff --git a/net/core/filter.c b/net/core/filter.c index ba019ded773d..6957d3449f2c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -600,8 +600,7 @@ static int bpf_convert_filter(struct sock_filter *prog, int len, if (new_prog) { first_insn = new_prog->insnsi; - addrs = kcalloc(len, sizeof(*addrs), - GFP_KERNEL | __GFP_NOWARN); + addrs = kzalloc_objs(*addrs, len, GFP_KERNEL | __GFP_NOWARN); if (!addrs) return -ENOMEM; } @@ -1162,7 +1161,7 @@ static int bpf_prog_store_orig_filter(struct bpf_prog *fp, unsigned int fsize = bpf_classic_proglen(fprog); struct sock_fprog_kern *fkprog; - fp->orig_prog = kmalloc(sizeof(*fkprog), GFP_KERNEL); + fp->orig_prog = kmalloc_obj(*fkprog, GFP_KERNEL); if (!fp->orig_prog) return -ENOMEM; @@ -1482,7 +1481,7 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk) { struct sk_filter *fp, *old_fp; - fp = kmalloc(sizeof(*fp), GFP_KERNEL); + fp = kmalloc_obj(*fp, GFP_KERNEL); if (!fp) return -ENOMEM; diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index bc5169482710..630f94e2c2c1 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -12,8 +12,7 @@ struct flow_rule *flow_rule_alloc(unsigned int num_actions) struct flow_rule *rule; int i; - rule = kzalloc(struct_size(rule, action.entries, num_actions), - GFP_KERNEL); + rule = kzalloc_flex(*rule, action.entries, num_actions, GFP_KERNEL); if (!rule) return NULL; @@ -33,8 +32,8 @@ struct flow_offload_action *offload_action_alloc(unsigned int num_actions) struct flow_offload_action *fl_action; int i; - fl_action = kzalloc(struct_size(fl_action, action.entries, num_actions), - GFP_KERNEL); + fl_action = kzalloc_flex(*fl_action, action.entries, num_actions, + GFP_KERNEL); if (!fl_action) return NULL; @@ -264,7 +263,7 @@ struct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb, { struct flow_block_cb *block_cb; - block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL); + block_cb = kzalloc_obj(*block_cb, GFP_KERNEL); if (!block_cb) return ERR_PTR(-ENOMEM); @@ -391,7 +390,7 @@ static struct flow_indr_dev *flow_indr_dev_alloc(flow_indr_block_bind_cb_t *cb, { struct flow_indr_dev *indr_dev; - indr_dev = kmalloc(sizeof(*indr_dev), GFP_KERNEL); + indr_dev = kmalloc_obj(*indr_dev, GFP_KERNEL); if (!indr_dev) return NULL; @@ -571,7 +570,7 @@ static int indir_dev_add(void *data, struct net_device *dev, struct Qdisc *sch, if (info) return -EEXIST; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc_obj(*info, GFP_KERNEL); if (!info) return -ENOMEM; diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index f112156db587..64d0d55fa2b2 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -154,7 +154,7 @@ int gen_new_estimator(struct gnet_stats_basic_sync *bstats, if (parm->ewma_log == 0 || parm->ewma_log >= 31) return -EINVAL; - est = kzalloc(sizeof(*est), GFP_KERNEL); + est = kzalloc_obj(*est, GFP_KERNEL); if (!est) return -ENOBUFS; diff --git a/net/core/gro_cells.c b/net/core/gro_cells.c index a725d21159a6..1b84385c04bd 100644 --- a/net/core/gro_cells.c +++ b/net/core/gro_cells.c @@ -132,7 +132,7 @@ void gro_cells_destroy(struct gro_cells *gcells) * because we might be called from cleanup_net(), and we * definitely do not want to block this critical task. */ - defer = kmalloc(sizeof(*defer), GFP_KERNEL | __GFP_NOWARN); + defer = kmalloc_obj(*defer, GFP_KERNEL | __GFP_NOWARN); if (likely(defer)) { defer->ptr = gcells->cells; call_rcu(&defer->rcu, percpu_free_defer_callback); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index e0897eb41c8d..a95cfe77f7f0 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -562,7 +562,7 @@ static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift) struct neigh_hash_table *ret; int i; - ret = kmalloc(sizeof(*ret), GFP_ATOMIC); + ret = kmalloc_obj(*ret, GFP_ATOMIC); if (!ret) return NULL; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index aef44e617361..7a040a9233e7 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -492,7 +492,7 @@ static struct net *net_alloc(void) goto out_free; #ifdef CONFIG_KEYS - net->key_domain = kzalloc(sizeof(struct key_tag), GFP_KERNEL); + net->key_domain = kzalloc_obj(struct key_tag, GFP_KERNEL); if (!net->key_domain) goto out_free_2; refcount_set(&net->key_domain->usage, 1); diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c index db9a5354f9de..9267880a0a17 100644 --- a/net/core/netclassid_cgroup.c +++ b/net/core/netclassid_cgroup.c @@ -32,7 +32,7 @@ cgrp_css_alloc(struct cgroup_subsys_state *parent_css) { struct cgroup_cls_state *cs; - cs = kzalloc(sizeof(*cs), GFP_KERNEL); + cs = kzalloc_obj(*cs, GFP_KERNEL); if (!cs) return ERR_PTR(-ENOMEM); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 09f72f10813c..70e458893ea5 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -565,7 +565,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) npinfo = rtnl_dereference(ndev->npinfo); if (!npinfo) { - npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); + npinfo = kmalloc_obj(*npinfo, GFP_KERNEL); if (!npinfo) { err = -ENOMEM; goto out; diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 8456dfbe2eb4..fb3cd2886e1f 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -135,7 +135,7 @@ cgrp_css_alloc(struct cgroup_subsys_state *parent_css) { struct cgroup_subsys_state *css; - css = kzalloc(sizeof(*css), GFP_KERNEL); + css = kzalloc_obj(*css, GFP_KERNEL); if (!css) return ERR_PTR(-ENOMEM); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b1ed55141d8a..da268fc45356 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -414,7 +414,7 @@ static int rtnl_register_internal(struct module *owner, if (!link) goto unlock; } else { - link = kzalloc(sizeof(*link), GFP_KERNEL); + link = kzalloc_obj(*link, GFP_KERNEL); if (!link) goto unlock; } @@ -3969,7 +3969,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, int ops_srcu_index; int ret; - tbs = kmalloc(sizeof(*tbs), GFP_KERNEL); + tbs = kmalloc_obj(*tbs, GFP_KERNEL); if (!tbs) return -ENOMEM; diff --git a/net/core/scm.c b/net/core/scm.c index cd87f66671aa..a29aa8fb8065 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -83,7 +83,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) if (!fpl) { - fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL_ACCOUNT); + fpl = kmalloc_obj(struct scm_fp_list, GFP_KERNEL_ACCOUNT); if (!fpl) return -ENOMEM; *fplp = fpl; diff --git a/net/core/selftests.c b/net/core/selftests.c index 8b81feb82c4a..248112bd51a8 100644 --- a/net/core/selftests.c +++ b/net/core/selftests.c @@ -237,7 +237,7 @@ static int __net_test_loopback(struct net_device *ndev, struct sk_buff *skb = NULL; int ret = 0; - tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL); + tpriv = kzalloc_obj(*tpriv, GFP_KERNEL); if (!tpriv) return -ENOMEM; diff --git a/net/core/skmsg.c b/net/core/skmsg.c index ddde93dd8bc6..2e26174c9919 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -522,7 +522,7 @@ static struct sk_msg *alloc_sk_msg(gfp_t gfp) { struct sk_msg *msg; - msg = kzalloc(sizeof(*msg), gfp | __GFP_NOWARN); + msg = kzalloc_obj(*msg, gfp | __GFP_NOWARN); if (unlikely(!msg)) return NULL; sg_init_marker(msg->sg.data, NR_MSG_FRAG_IDS); diff --git a/net/core/sock.c b/net/core/sock.c index 693e6d80f501..b62e509e06d9 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1097,7 +1097,7 @@ sock_devmem_dontneed(struct sock *sk, sockptr_t optval, unsigned int optlen) return -EINVAL; num_tokens = optlen / sizeof(*tokens); - tokens = kvmalloc_array(num_tokens, sizeof(*tokens), GFP_KERNEL); + tokens = kvmalloc_objs(*tokens, num_tokens, GFP_KERNEL); if (!tokens) return -ENOMEM; diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 026ce9bd9e5e..c83335c62360 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -177,7 +177,7 @@ void sock_diag_broadcast_destroy(struct sock *sk) { /* Note, this function is often called from an interrupt context. */ struct broadcast_sk *bsk = - kmalloc(sizeof(struct broadcast_sk), GFP_ATOMIC); + kmalloc_obj(struct broadcast_sk, GFP_ATOMIC); if (!bsk) return sk_destruct(sk); bsk->sk = sk; diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 5947b38e4f8b..b0e96337a269 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -1858,7 +1858,7 @@ int sock_map_link_create(const union bpf_attr *attr, struct bpf_prog *prog) goto out; } - sockmap_link = kzalloc(sizeof(*sockmap_link), GFP_USER); + sockmap_link = kzalloc_obj(*sockmap_link, GFP_USER); if (!sockmap_link) { ret = -ENOMEM; goto out; diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c index 4211710393a8..29948cb44b7d 100644 --- a/net/core/sock_reuseport.c +++ b/net/core/sock_reuseport.c @@ -175,7 +175,7 @@ static struct sock_reuseport *__reuseport_alloc(unsigned int max_socks) { struct sock_reuseport *reuse; - reuse = kzalloc(struct_size(reuse, socks, max_socks), GFP_ATOMIC); + reuse = kzalloc_flex(*reuse, socks, max_socks, GFP_ATOMIC); if (!reuse) return NULL; diff --git a/net/core/xdp.c b/net/core/xdp.c index fee6d080ee85..8c65d7dafd89 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -214,7 +214,7 @@ static int __mem_id_init_hash_table(void) if (unlikely(mem_id_init)) return 0; - rht = kzalloc(sizeof(*rht), GFP_KERNEL); + rht = kzalloc_obj(*rht, GFP_KERNEL); if (!rht) return -ENOMEM; @@ -297,7 +297,7 @@ static struct xdp_mem_allocator *__xdp_reg_mem_model(struct xdp_mem_info *mem, return ERR_PTR(ret); } - xdp_alloc = kzalloc(sizeof(*xdp_alloc), gfp); + xdp_alloc = kzalloc_obj(*xdp_alloc, gfp); if (!xdp_alloc) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From bf4afc53b77aeaa48b5409da5c8da6bb4eff7f43 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 21 Feb 2026 16:37:42 -0800 Subject: Convert 'alloc_obj' family to use the new default GFP_KERNEL argument This was done entirely with mindless brute force, using git grep -l '\ --- net/core/dev.c | 8 ++++---- net/core/drop_monitor.c | 2 +- net/core/failover.c | 2 +- net/core/filter.c | 4 ++-- net/core/flow_offload.c | 6 +++--- net/core/gen_estimator.c | 2 +- net/core/net_namespace.c | 2 +- net/core/netclassid_cgroup.c | 2 +- net/core/netpoll.c | 2 +- net/core/netprio_cgroup.c | 2 +- net/core/rtnetlink.c | 4 ++-- net/core/selftests.c | 2 +- net/core/sock.c | 2 +- net/core/xdp.c | 2 +- 14 files changed, 21 insertions(+), 21 deletions(-) (limited to 'net/core') diff --git a/net/core/dev.c b/net/core/dev.c index 525cf5952101..b91aabb18bbe 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -273,7 +273,7 @@ static struct netdev_name_node *netdev_name_node_alloc(struct net_device *dev, { struct netdev_name_node *name_node; - name_node = kmalloc_obj(*name_node, GFP_KERNEL); + name_node = kmalloc_obj(*name_node); if (!name_node) return NULL; INIT_HLIST_NODE(&name_node->hlist); @@ -8693,7 +8693,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev, return 0; } - adj = kmalloc_obj(*adj, GFP_KERNEL); + adj = kmalloc_obj(*adj); if (!adj) return -ENOMEM; @@ -11940,7 +11940,7 @@ struct netdev_queue *dev_ingress_queue_create(struct net_device *dev) #ifdef CONFIG_NET_CLS_ACT if (queue) return queue; - queue = kzalloc_obj(*queue, GFP_KERNEL); + queue = kzalloc_obj(*queue); if (!queue) return NULL; netdev_init_one_queue(dev, queue, NULL); @@ -12857,7 +12857,7 @@ static struct hlist_head * __net_init netdev_create_hash(void) int i; struct hlist_head *hash; - hash = kmalloc_objs(*hash, NETDEV_HASHENTRIES, GFP_KERNEL); + hash = kmalloc_objs(*hash, NETDEV_HASHENTRIES); if (hash != NULL) for (i = 0; i < NETDEV_HASHENTRIES; i++) INIT_HLIST_HEAD(&hash[i]); diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 99f9d4dfb866..da0d231e1952 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -1583,7 +1583,7 @@ static int dropmon_net_event(struct notifier_block *ev_block, case NETDEV_REGISTER: if (WARN_ON_ONCE(rtnl_dereference(dev->dm_private))) break; - stat = kzalloc_obj(*stat, GFP_KERNEL); + stat = kzalloc_obj(*stat); if (!stat) break; diff --git a/net/core/failover.c b/net/core/failover.c index fcc04e6995d0..0eb2e0ec875b 100644 --- a/net/core/failover.c +++ b/net/core/failover.c @@ -247,7 +247,7 @@ struct failover *failover_register(struct net_device *dev, if (dev->type != ARPHRD_ETHER) return ERR_PTR(-EINVAL); - failover = kzalloc_obj(*failover, GFP_KERNEL); + failover = kzalloc_obj(*failover); if (!failover) return ERR_PTR(-ENOMEM); diff --git a/net/core/filter.c b/net/core/filter.c index 6957d3449f2c..0d5d5a17acb2 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1161,7 +1161,7 @@ static int bpf_prog_store_orig_filter(struct bpf_prog *fp, unsigned int fsize = bpf_classic_proglen(fprog); struct sock_fprog_kern *fkprog; - fp->orig_prog = kmalloc_obj(*fkprog, GFP_KERNEL); + fp->orig_prog = kmalloc_obj(*fkprog); if (!fp->orig_prog) return -ENOMEM; @@ -1481,7 +1481,7 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk) { struct sk_filter *fp, *old_fp; - fp = kmalloc_obj(*fp, GFP_KERNEL); + fp = kmalloc_obj(*fp); if (!fp) return -ENOMEM; diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index 630f94e2c2c1..c55bf9d5ac8c 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -263,7 +263,7 @@ struct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb, { struct flow_block_cb *block_cb; - block_cb = kzalloc_obj(*block_cb, GFP_KERNEL); + block_cb = kzalloc_obj(*block_cb); if (!block_cb) return ERR_PTR(-ENOMEM); @@ -390,7 +390,7 @@ static struct flow_indr_dev *flow_indr_dev_alloc(flow_indr_block_bind_cb_t *cb, { struct flow_indr_dev *indr_dev; - indr_dev = kmalloc_obj(*indr_dev, GFP_KERNEL); + indr_dev = kmalloc_obj(*indr_dev); if (!indr_dev) return NULL; @@ -570,7 +570,7 @@ static int indir_dev_add(void *data, struct net_device *dev, struct Qdisc *sch, if (info) return -EEXIST; - info = kzalloc_obj(*info, GFP_KERNEL); + info = kzalloc_obj(*info); if (!info) return -ENOMEM; diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index 64d0d55fa2b2..c34e58c6c3e6 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -154,7 +154,7 @@ int gen_new_estimator(struct gnet_stats_basic_sync *bstats, if (parm->ewma_log == 0 || parm->ewma_log >= 31) return -EINVAL; - est = kzalloc_obj(*est, GFP_KERNEL); + est = kzalloc_obj(*est); if (!est) return -ENOBUFS; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 7a040a9233e7..1057d16d5dd2 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -492,7 +492,7 @@ static struct net *net_alloc(void) goto out_free; #ifdef CONFIG_KEYS - net->key_domain = kzalloc_obj(struct key_tag, GFP_KERNEL); + net->key_domain = kzalloc_obj(struct key_tag); if (!net->key_domain) goto out_free_2; refcount_set(&net->key_domain->usage, 1); diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c index 9267880a0a17..e1e30f0b60cd 100644 --- a/net/core/netclassid_cgroup.c +++ b/net/core/netclassid_cgroup.c @@ -32,7 +32,7 @@ cgrp_css_alloc(struct cgroup_subsys_state *parent_css) { struct cgroup_cls_state *cs; - cs = kzalloc_obj(*cs, GFP_KERNEL); + cs = kzalloc_obj(*cs); if (!cs) return ERR_PTR(-ENOMEM); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 70e458893ea5..a8558a52884f 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -565,7 +565,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) npinfo = rtnl_dereference(ndev->npinfo); if (!npinfo) { - npinfo = kmalloc_obj(*npinfo, GFP_KERNEL); + npinfo = kmalloc_obj(*npinfo); if (!npinfo) { err = -ENOMEM; goto out; diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index fb3cd2886e1f..76c33ab44761 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -135,7 +135,7 @@ cgrp_css_alloc(struct cgroup_subsys_state *parent_css) { struct cgroup_subsys_state *css; - css = kzalloc_obj(*css, GFP_KERNEL); + css = kzalloc_obj(*css); if (!css) return ERR_PTR(-ENOMEM); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index da268fc45356..dad4b1054955 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -414,7 +414,7 @@ static int rtnl_register_internal(struct module *owner, if (!link) goto unlock; } else { - link = kzalloc_obj(*link, GFP_KERNEL); + link = kzalloc_obj(*link); if (!link) goto unlock; } @@ -3969,7 +3969,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, int ops_srcu_index; int ret; - tbs = kmalloc_obj(*tbs, GFP_KERNEL); + tbs = kmalloc_obj(*tbs); if (!tbs) return -ENOMEM; diff --git a/net/core/selftests.c b/net/core/selftests.c index 248112bd51a8..0a203d3fb9dc 100644 --- a/net/core/selftests.c +++ b/net/core/selftests.c @@ -237,7 +237,7 @@ static int __net_test_loopback(struct net_device *ndev, struct sk_buff *skb = NULL; int ret = 0; - tpriv = kzalloc_obj(*tpriv, GFP_KERNEL); + tpriv = kzalloc_obj(*tpriv); if (!tpriv) return -ENOMEM; diff --git a/net/core/sock.c b/net/core/sock.c index b62e509e06d9..5976100a9d55 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1097,7 +1097,7 @@ sock_devmem_dontneed(struct sock *sk, sockptr_t optval, unsigned int optlen) return -EINVAL; num_tokens = optlen / sizeof(*tokens); - tokens = kvmalloc_objs(*tokens, num_tokens, GFP_KERNEL); + tokens = kvmalloc_objs(*tokens, num_tokens); if (!tokens) return -ENOMEM; diff --git a/net/core/xdp.c b/net/core/xdp.c index 8c65d7dafd89..9890a30584ba 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -214,7 +214,7 @@ static int __mem_id_init_hash_table(void) if (unlikely(mem_id_init)) return 0; - rht = kzalloc_obj(*rht, GFP_KERNEL); + rht = kzalloc_obj(*rht); if (!rht) return -ENOMEM; -- cgit v1.2.3 From 323bbfcf1ef8836d0d2ad9e2c1f1c684f0e3b5b3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 21 Feb 2026 17:06:51 -0800 Subject: Convert 'alloc_flex' family to use the new default GFP_KERNEL argument This is the exact same thing as the 'alloc_obj()' version, only much smaller because there are a lot fewer users of the *alloc_flex() interface. As with alloc_obj() version, this was done entirely with mindless brute force, using the same script, except using 'flex' in the pattern rather than 'objs*'. Signed-off-by: Linus Torvalds --- net/core/bpf_sk_storage.c | 2 +- net/core/dev.c | 2 +- net/core/flow_offload.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net/core') diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index 8165d606bfd4..f8338acebf07 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -500,7 +500,7 @@ bpf_sk_storage_diag_alloc(const struct nlattr *nla_stgs) nr_maps++; } - diag = kzalloc_flex(*diag, maps, nr_maps, GFP_KERNEL); + diag = kzalloc_flex(*diag, maps, nr_maps); if (!diag) return ERR_PTR(-ENOMEM); diff --git a/net/core/dev.c b/net/core/dev.c index b91aabb18bbe..27e5fcbaa2d6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6510,7 +6510,7 @@ struct flush_backlogs { static struct flush_backlogs *flush_backlogs_alloc(void) { - return kmalloc_flex(struct flush_backlogs, w, nr_cpu_ids, GFP_KERNEL); + return kmalloc_flex(struct flush_backlogs, w, nr_cpu_ids); } static struct flush_backlogs *flush_backlogs_fallback; diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index c55bf9d5ac8c..c0a9c5b3c977 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -12,7 +12,7 @@ struct flow_rule *flow_rule_alloc(unsigned int num_actions) struct flow_rule *rule; int i; - rule = kzalloc_flex(*rule, action.entries, num_actions, GFP_KERNEL); + rule = kzalloc_flex(*rule, action.entries, num_actions); if (!rule) return NULL; -- cgit v1.2.3 From 32a92f8c89326985e05dce8b22d3f0aa07a3e1bd Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 21 Feb 2026 20:03:00 -0800 Subject: Convert more 'alloc_obj' cases to default GFP_KERNEL arguments This converts some of the visually simpler cases that have been split over multiple lines. I only did the ones that are easy to verify the resulting diff by having just that final GFP_KERNEL argument on the next line. Somebody should probably do a proper coccinelle script for this, but for me the trivial script actually resulted in an assertion failure in the middle of the script. I probably had made it a bit _too_ trivial. So after fighting that far a while I decided to just do some of the syntactically simpler cases with variations of the previous 'sed' scripts. The more syntactically complex multi-line cases would mostly really want whitespace cleanup anyway. Signed-off-by: Linus Torvalds --- net/core/dev.c | 3 +-- net/core/drop_monitor.c | 3 +-- net/core/flow_offload.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'net/core') diff --git a/net/core/dev.c b/net/core/dev.c index 27e5fcbaa2d6..6ff4256700e6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -9133,8 +9133,7 @@ static int netdev_offload_xstats_enable_l3(struct net_device *dev, int err; int rc; - dev->offload_xstats_l3 = kzalloc_obj(*dev->offload_xstats_l3, - GFP_KERNEL); + dev->offload_xstats_l3 = kzalloc_obj(*dev->offload_xstats_l3); if (!dev->offload_xstats_l3) return -ENOMEM; diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index da0d231e1952..f23cea9e1aaf 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -306,8 +306,7 @@ net_dm_hw_reset_per_cpu_data(struct per_cpu_dm_data *hw_data) struct net_dm_hw_entries *hw_entries; unsigned long flags; - hw_entries = kzalloc_flex(*hw_entries, entries, dm_hit_limit, - GFP_KERNEL); + hw_entries = kzalloc_flex(*hw_entries, entries, dm_hit_limit); if (!hw_entries) { /* If the memory allocation failed, we try to perform another * allocation in 1/10 second. Otherwise, the probe function diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index c0a9c5b3c977..5071d7fe6ce2 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -32,8 +32,7 @@ struct flow_offload_action *offload_action_alloc(unsigned int num_actions) struct flow_offload_action *fl_action; int i; - fl_action = kzalloc_flex(*fl_action, action.entries, num_actions, - GFP_KERNEL); + fl_action = kzalloc_flex(*fl_action, action.entries, num_actions); if (!fl_action) return NULL; -- cgit v1.2.3 From 189f164e573e18d9f8876dbd3ad8fcbe11f93037 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 21 Feb 2026 23:46:04 -0800 Subject: Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses Conversion performed via this Coccinelle script: // SPDX-License-Identifier: GPL-2.0-only // Options: --include-headers-for-types --all-includes --include-headers --keep-comments virtual patch @gfp depends on patch && !(file in "tools") && !(file in "samples")@ identifier ALLOC = {kmalloc_obj,kmalloc_objs,kmalloc_flex, kzalloc_obj,kzalloc_objs,kzalloc_flex, kvmalloc_obj,kvmalloc_objs,kvmalloc_flex, kvzalloc_obj,kvzalloc_objs,kvzalloc_flex}; @@ ALLOC(... - , GFP_KERNEL ) $ make coccicheck MODE=patch COCCI=gfp.cocci Build and boot tested x86_64 with Fedora 42's GCC and Clang: Linux version 6.19.0+ (user@host) (gcc (GCC) 15.2.1 20260123 (Red Hat 15.2.1-7), GNU ld version 2.44-12.fc42) #1 SMP PREEMPT_DYNAMIC 1970-01-01 Linux version 6.19.0+ (user@host) (clang version 20.1.8 (Fedora 20.1.8-4.fc42), LLD 20.1.8) #1 SMP PREEMPT_DYNAMIC 1970-01-01 Signed-off-by: Kees Cook Signed-off-by: Linus Torvalds --- net/core/devmem.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net/core') diff --git a/net/core/devmem.c b/net/core/devmem.c index 16a1949dbed1..8c9aad776bf4 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -242,8 +242,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, if (direction == DMA_TO_DEVICE) { binding->tx_vec = kvmalloc_objs(struct net_iov *, - dmabuf->size / PAGE_SIZE, - GFP_KERNEL); + dmabuf->size / PAGE_SIZE); if (!binding->tx_vec) { err = -ENOMEM; goto err_unmap; @@ -290,8 +289,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, } owner->area.niovs = kvmalloc_objs(*owner->area.niovs, - owner->area.num_niovs, - GFP_KERNEL); + owner->area.num_niovs); if (!owner->area.niovs) { err = -ENOMEM; goto err_free_chunks; -- cgit v1.2.3 From 8a8a9fac9efa6423fd74938b940cb7d731780718 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 20 Feb 2026 22:26:05 +0000 Subject: net: do not pass flow_id to set_rps_cpu() Blamed commit made the assumption that the RPS table for each receive queue would have the same size, and that it would not change. Compute flow_id in set_rps_cpu(), do not assume we can use the value computed by get_rps_cpu(). Otherwise we risk out-of-bound access and/or crashes. Fixes: 48aa30443e52 ("net: Cache hash and flow_id to avoid recalculation") Signed-off-by: Eric Dumazet Cc: Krishna Kumar Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260220222605.3468081-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'net/core') diff --git a/net/core/dev.c b/net/core/dev.c index 096b3ff13f6b..f3426385f1ba 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4992,8 +4992,7 @@ static bool rps_flow_is_active(struct rps_dev_flow *rflow, static struct rps_dev_flow * set_rps_cpu(struct net_device *dev, struct sk_buff *skb, - struct rps_dev_flow *rflow, u16 next_cpu, u32 hash, - u32 flow_id) + struct rps_dev_flow *rflow, u16 next_cpu, u32 hash) { if (next_cpu < nr_cpu_ids) { u32 head; @@ -5004,6 +5003,7 @@ set_rps_cpu(struct net_device *dev, struct sk_buff *skb, struct rps_dev_flow *tmp_rflow; unsigned int tmp_cpu; u16 rxq_index; + u32 flow_id; int rc; /* Should we steer this flow to a different hardware queue? */ @@ -5019,6 +5019,7 @@ set_rps_cpu(struct net_device *dev, struct sk_buff *skb, if (!flow_table) goto out; + flow_id = rfs_slot(hash, flow_table); tmp_rflow = &flow_table->flows[flow_id]; tmp_cpu = READ_ONCE(tmp_rflow->cpu); @@ -5066,7 +5067,6 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, struct rps_dev_flow_table *flow_table; struct rps_map *map; int cpu = -1; - u32 flow_id; u32 tcpu; u32 hash; @@ -5113,8 +5113,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, /* OK, now we know there is a match, * we can look at the local (per receive queue) flow table */ - flow_id = rfs_slot(hash, flow_table); - rflow = &flow_table->flows[flow_id]; + rflow = &flow_table->flows[rfs_slot(hash, flow_table)]; tcpu = rflow->cpu; /* @@ -5133,8 +5132,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, ((int)(READ_ONCE(per_cpu(softnet_data, tcpu).input_queue_head) - rflow->last_qtail)) >= 0)) { tcpu = next_cpu; - rflow = set_rps_cpu(dev, skb, rflow, next_cpu, hash, - flow_id); + rflow = set_rps_cpu(dev, skb, rflow, next_cpu, hash); } if (tcpu < nr_cpu_ids && cpu_online(tcpu)) { -- cgit v1.2.3 From 983512f3a87fd8dc4c94dfa6b596b6e57df5aad7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 20 Feb 2026 19:38:58 +0100 Subject: net: Drop the lock in skb_may_tx_timestamp() skb_may_tx_timestamp() may acquire sock::sk_callback_lock. The lock must not be taken in IRQ context, only softirq is okay. A few drivers receive the timestamp via a dedicated interrupt and complete the TX timestamp from that handler. This will lead to a deadlock if the lock is already write-locked on the same CPU. Taking the lock can be avoided. The socket (pointed by the skb) will remain valid until the skb is released. The ->sk_socket and ->file member will be set to NULL once the user closes the socket which may happen before the timestamp arrives. If we happen to observe the pointer while the socket is closing but before the pointer is set to NULL then we may use it because both pointer (and the file's cred member) are RCU freed. Drop the lock. Use READ_ONCE() to obtain the individual pointer. Add a matching WRITE_ONCE() where the pointer are cleared. Link: https://lore.kernel.org/all/20260205145104.iWinkXHv@linutronix.de Fixes: b245be1f4db1a ("net-timestamp: no-payload only sysctl") Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Willem de Bruijn Reviewed-by: Jason Xing Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260220183858.N4ERjFW6@linutronix.de Signed-off-by: Paolo Abeni --- net/core/skbuff.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'net/core') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index dc47d3efc72e..0e217041958a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5590,15 +5590,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb, static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly) { - bool ret; + struct socket *sock; + struct file *file; + bool ret = false; if (likely(tsonly || READ_ONCE(sock_net(sk)->core.sysctl_tstamp_allow_data))) return true; - read_lock_bh(&sk->sk_callback_lock); - ret = sk->sk_socket && sk->sk_socket->file && - file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW); - read_unlock_bh(&sk->sk_callback_lock); + /* The sk pointer remains valid as long as the skb is. The sk_socket and + * file pointer may become NULL if the socket is closed. Both structures + * (including file->cred) are RCU freed which means they can be accessed + * within a RCU read section. + */ + rcu_read_lock(); + sock = READ_ONCE(sk->sk_socket); + if (!sock) + goto out; + file = READ_ONCE(sock->file); + if (!file) + goto out; + ret = file_ns_capable(file, &init_user_ns, CAP_NET_RAW); +out: + rcu_read_unlock(); return ret; } -- cgit v1.2.3 From 7aa767d0d3d04e50ae94e770db7db8197f666970 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 23 Feb 2026 15:51:00 -0800 Subject: net: consume xmit errors of GSO frames udpgro_frglist.sh and udpgro_bench.sh are the flakiest tests currently in NIPA. They fail in the same exact way, TCP GRO test stalls occasionally and the test gets killed after 10min. These tests use veth to simulate GRO. They attach a trivial ("return XDP_PASS;") XDP program to the veth to force TSO off and NAPI on. Digging into the failure mode we can see that the connection is completely stuck after a burst of drops. The sender's snd_nxt is at sequence number N [1], but the receiver claims to have received (rcv_nxt) up to N + 3 * MSS [2]. Last piece of the puzzle is that senders rtx queue is not empty (let's say the block in the rtx queue is at sequence number N - 4 * MSS [3]). In this state, sender sends a retransmission from the rtx queue with a single segment, and sequence numbers N-4*MSS:N-3*MSS [3]. Receiver sees it and responds with an ACK all the way up to N + 3 * MSS [2]. But sender will reject this ack as TCP_ACK_UNSENT_DATA because it has no recollection of ever sending data that far out [1]. And we are stuck. The root cause is the mess of the xmit return codes. veth returns an error when it can't xmit a frame. We end up with a loss event like this: ------------------------------------------------- | GSO super frame 1 | GSO super frame 2 | |-----------------------------------------------| | seg | seg | seg | seg | seg | seg | seg | seg | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ------------------------------------------------- x ok ok | ok ok ok \\ snd_nxt "x" means packet lost by veth, and "ok" means it went thru. Since veth has TSO disabled in this test it sees individual segments. Segment 1 is on the retransmit queue and will be resent. So why did the sender not advance snd_nxt even tho it clearly did send up to seg 8? tcp_write_xmit() interprets the return code from the core to mean that data has not been sent at all. Since TCP deals with GSO super frames, not individual segment the crux of the problem is that loss of a single segment can be interpreted as loss of all. TCP only sees the last return code for the last segment of the GSO frame (in <> brackets in the diagram above). Of course for the problem to occur we need a setup or a device without a Qdisc. Otherwise Qdisc layer disconnects the protocol layer from the device errors completely. We have multiple ways to fix this. 1) make veth not return an error when it lost a packet. While this is what I think we did in the past, the issue keeps reappearing and it's annoying to debug. The game of whack a mole is not great. 2) fix the damn return codes We only talk about NETDEV_TX_OK and NETDEV_TX_BUSY in the documentation, so maybe we should make the return code from ndo_start_xmit() a boolean. I like that the most, but perhaps some ancient, not-really-networking protocol would suffer. 3) make TCP ignore the errors It is not entirely clear to me what benefit TCP gets from interpreting the result of ip_queue_xmit()? Specifically once the connection is established and we're pushing data - packet loss is just packet loss? 4) this fix Ignore the rc in the Qdisc-less+GSO case, since it's unreliable. We already always return OK in the TCQ_F_CAN_BYPASS case. In the Qdisc-less case let's be a bit more conservative and only mask the GSO errors. This path is taken by non-IP-"networks" like CAN, MCTP etc, so we could regress some ancient thing. This is the simplest, but also maybe the hackiest fix? Similar fix has been proposed by Eric in the past but never committed because original reporter was working with an OOT driver and wasn't providing feedback (see Link). Link: https://lore.kernel.org/CANn89iJcLepEin7EtBETrZ36bjoD9LrR=k4cfwWh046GB+4f9A@mail.gmail.com Fixes: 1f59533f9ca5 ("qdisc: validate frames going through the direct_xmit path") Signed-off-by: Jakub Kicinski Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260223235100.108939-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- net/core/dev.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'net/core') diff --git a/net/core/dev.c b/net/core/dev.c index f3426385f1ba..3d2d3b18915c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4822,6 +4822,8 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) * to -1 or to their cpu id, but not to our id. */ if (READ_ONCE(txq->xmit_lock_owner) != cpu) { + bool is_list = false; + if (dev_xmit_recursion()) goto recursion_alert; @@ -4832,17 +4834,28 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) HARD_TX_LOCK(dev, txq, cpu); if (!netif_xmit_stopped(txq)) { + is_list = !!skb->next; + dev_xmit_recursion_inc(); skb = dev_hard_start_xmit(skb, dev, txq, &rc); dev_xmit_recursion_dec(); - if (dev_xmit_complete(rc)) { - HARD_TX_UNLOCK(dev, txq); - goto out; - } + + /* GSO segments a single SKB into + * a list of frames. TCP expects error + * to mean none of the data was sent. + */ + if (is_list) + rc = NETDEV_TX_OK; } HARD_TX_UNLOCK(dev, txq); + if (!skb) /* xmit completed */ + goto out; + net_crit_ratelimited("Virtual device %s asks to queue packet!\n", dev->name); + /* NETDEV_TX_BUSY or queue was stopped */ + if (!is_list) + rc = -ENETDOWN; } else { /* Recursion is detected! It is possible, * unfortunately @@ -4850,10 +4863,10 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) recursion_alert: net_crit_ratelimited("Dead loop on virtual device %s, fix it urgently!\n", dev->name); + rc = -ENETDOWN; } } - rc = -ENETDOWN; rcu_read_unlock_bh(); dev_core_stats_tx_dropped_inc(dev); -- cgit v1.2.3