From nobody Sat Feb 7 17:48:45 2026 Received: from mail-4318.protonmail.ch (mail-4318.protonmail.ch [185.70.43.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A18E815B998; Tue, 20 Jan 2026 00:48:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768870123; cv=none; b=DatpCKBhrPXy4dQewWagHiWiQDobzuCnxv1RQdIz8fPLw5jP/AZk0r7ZQnsZ3+hgFls4J3VuiVRZim86RKEP2mBJQkx1axbEwJZYozRQGPNN2Ofp8/dSW7pgCrOnlAs4uy3Odxrq00d/3hTUVAoroHpTnPkTVAK1J0isTIq/zYg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768870123; c=relaxed/simple; bh=gSeCMJbvUQZG5Xrmjv++axPBgJMWw+rdkw44cLMZBUY=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=u5yzQ38dS9hVFZ+2ZxKuqMX6rJam/vFKPCNrM7FEpciFCV9eBABOBU3DjxYxftqtjXSWZpDA5CqllXa5OvzhpzKpYevMi4L1Y6+PUinN6nMrX5snPZgXaygpdrlIftCtCwY7QqXJLDImx9YJHOmLsLEg8sQMZDKCFjt9F5xWxIk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=1g4.org; spf=pass smtp.mailfrom=1g4.org; dkim=pass (2048-bit key) header.d=1g4.org header.i=@1g4.org header.b=HDr6qax0; arc=none smtp.client-ip=185.70.43.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=1g4.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=1g4.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=1g4.org header.i=@1g4.org header.b="HDr6qax0" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1g4.org; s=protonmail2; t=1768870111; x=1769129311; bh=77VKY8jZD75pdc5UGRD6x7vULxMsng4rjkIcHZvOR8o=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=HDr6qax0YZJXscBh1UXBbTMh8CHKZdpFtyK1oq2SRv8tYkOByxadI6R+UN+MQfoZ1 41HiuvsnRat6Zp+Shvr1q0ZSr2ZB5FZE8rR5e/qQS6NVxcbRrV+2OD+4jNrdJkFlSf M1NkocegtXAvdP/KIwTqF0rs8ZSN+PnDnZtXRNu+zqPCiOD3jBd1y+N7mKZf92F4rL mzt9LURXrB/icV2QStdmF5qw9Quq40r0vkbBbfpClj3sEqmqbZcf306l9b9ZZh4cFd dzMhJbiEvNTToY5a93lI9g1Fiy3HkAhOIunhqD+Zbz84Mr3SzMx7P3xYPtk2uo85c5 i/UmktbhlH8Pw== Date: Tue, 20 Jan 2026 00:48:29 +0000 To: netdev@vger.kernel.org From: Paul Moses Cc: Jamal Hadi Salim , Cong Wang , Jiri Pirko , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-kernel@vger.kernel.org, Paul Moses , stable@vger.kernel.org Subject: [PATCH 1/2] net/sched: act_gate: fix schedule updates with RCU swap Message-ID: <20260120004720.1886632-2-p@1g4.org> In-Reply-To: <20260120004720.1886632-1-p@1g4.org> References: <20260120004720.1886632-1-p@1g4.org> Feedback-ID: 8253658:user:proton X-Pm-Message-ID: b24c6853fe8e0db61c48a9e588b62af7daef29b6 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Switch act_gate parameters to an RCU-protected pointer and update schedule changes using a prepare-then-swap pattern. This avoids races between the timer/data paths and configuration updates, and cancels the hrtimer before swapping schedules. A gate action replace could free and swap schedules while the hrtimer callback or data path still dereferences the old entries, leaving a use-after-free window during updates. The deferred swap and RCU free close that window. A reproducer is available on request. Also clear params on early error for newly created actions to avoid leaving a dangling reference. Fixes: a51c328df310 ("net: qos: introduce a gate control flow action") Cc: stable@vger.kernel.org Signed-off-by: Paul Moses --- include/net/tc_act/tc_gate.h | 49 +++++- net/sched/act_gate.c | 298 +++++++++++++++++++++++++++-------- 2 files changed, 270 insertions(+), 77 deletions(-) diff --git a/include/net/tc_act/tc_gate.h b/include/net/tc_act/tc_gate.h index c1a67149c6b62..a2a24a62dff85 100644 --- a/include/net/tc_act/tc_gate.h +++ b/include/net/tc_act/tc_gate.h @@ -32,6 +32,7 @@ struct tcf_gate_params { s32 tcfg_clockid; size_t num_entries; struct list_head entries; + struct rcu_head rcu; }; =20 #define GATE_ACT_GATE_OPEN BIT(0) @@ -39,7 +40,7 @@ struct tcf_gate_params { =20 struct tcf_gate { struct tc_action common; - struct tcf_gate_params param; + struct tcf_gate_params __rcu *param; u8 current_gate_status; ktime_t current_close_time; u32 current_entry_octets; @@ -53,45 +54,75 @@ struct tcf_gate { =20 static inline s32 tcf_gate_prio(const struct tc_action *a) { + struct tcf_gate *gact =3D to_gate(a); + struct tcf_gate_params *p; s32 tcfg_prio; =20 - tcfg_prio =3D to_gate(a)->param.tcfg_priority; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&a->tcfa_lock) || + lockdep_is_held(&gact->tcf_lock) || + lockdep_rtnl_is_held()); + tcfg_prio =3D p->tcfg_priority; =20 return tcfg_prio; } =20 static inline u64 tcf_gate_basetime(const struct tc_action *a) { + struct tcf_gate *gact =3D to_gate(a); + struct tcf_gate_params *p; u64 tcfg_basetime; =20 - tcfg_basetime =3D to_gate(a)->param.tcfg_basetime; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&a->tcfa_lock) || + lockdep_is_held(&gact->tcf_lock) || + lockdep_rtnl_is_held()); + tcfg_basetime =3D p->tcfg_basetime; =20 return tcfg_basetime; } =20 static inline u64 tcf_gate_cycletime(const struct tc_action *a) { + struct tcf_gate *gact =3D to_gate(a); + struct tcf_gate_params *p; u64 tcfg_cycletime; =20 - tcfg_cycletime =3D to_gate(a)->param.tcfg_cycletime; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&a->tcfa_lock) || + lockdep_is_held(&gact->tcf_lock) || + lockdep_rtnl_is_held()); + tcfg_cycletime =3D p->tcfg_cycletime; =20 return tcfg_cycletime; } =20 static inline u64 tcf_gate_cycletimeext(const struct tc_action *a) { + struct tcf_gate *gact =3D to_gate(a); + struct tcf_gate_params *p; u64 tcfg_cycletimeext; =20 - tcfg_cycletimeext =3D to_gate(a)->param.tcfg_cycletime_ext; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&a->tcfa_lock) || + lockdep_is_held(&gact->tcf_lock) || + lockdep_rtnl_is_held()); + tcfg_cycletimeext =3D p->tcfg_cycletime_ext; =20 return tcfg_cycletimeext; } =20 static inline u32 tcf_gate_num_entries(const struct tc_action *a) { + struct tcf_gate *gact =3D to_gate(a); + struct tcf_gate_params *p; u32 num_entries; =20 - num_entries =3D to_gate(a)->param.num_entries; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&a->tcfa_lock) || + lockdep_is_held(&gact->tcf_lock) || + lockdep_rtnl_is_held()); + num_entries =3D p->num_entries; =20 return num_entries; } @@ -100,12 +131,16 @@ static inline struct action_gate_entry *tcf_gate_get_list(const struct tc_action *a) { struct action_gate_entry *oe; + struct tcf_gate *gact =3D to_gate(a); struct tcf_gate_params *p; struct tcfg_gate_entry *entry; u32 num_entries; int i =3D 0; =20 - p =3D &to_gate(a)->param; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&a->tcfa_lock) || + lockdep_is_held(&gact->tcf_lock) || + lockdep_rtnl_is_held()); num_entries =3D p->num_entries; =20 list_for_each_entry(entry, &p->entries, list) diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index c1f75f2727576..3ee07c3deaf97 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -32,9 +33,10 @@ static ktime_t gate_get_time(struct tcf_gate *gact) return KTIME_MAX; } =20 -static void gate_get_start_time(struct tcf_gate *gact, ktime_t *start) +static void gate_get_start_time(struct tcf_gate *gact, + struct tcf_gate_params *param, + ktime_t *start) { - struct tcf_gate_params *param =3D &gact->param; ktime_t now, base, cycle; u64 n; =20 @@ -69,12 +71,14 @@ static enum hrtimer_restart gate_timer_func(struct hrti= mer *timer) { struct tcf_gate *gact =3D container_of(timer, struct tcf_gate, hitimer); - struct tcf_gate_params *p =3D &gact->param; + struct tcf_gate_params *p; struct tcfg_gate_entry *next; ktime_t close_time, now; =20 spin_lock(&gact->tcf_lock); =20 + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); next =3D gact->next_entry; =20 /* cycle start, clear pending bit, clear total octets */ @@ -225,6 +229,14 @@ static void release_entry_list(struct list_head *entri= es) } } =20 +static void tcf_gate_params_release(struct rcu_head *rcu) +{ + struct tcf_gate_params *p =3D container_of(rcu, struct tcf_gate_params, r= cu); + + release_entry_list(&p->entries); + kfree(p); +} + static int parse_gate_list(struct nlattr *list_attr, struct tcf_gate_params *sched, struct netlink_ext_ack *extack) @@ -270,24 +282,12 @@ static int parse_gate_list(struct nlattr *list_attr, return err; } =20 -static void gate_setup_timer(struct tcf_gate *gact, u64 basetime, - enum tk_offsets tko, s32 clockid, - bool do_init) +static void gate_setup_timer(struct tcf_gate *gact, + enum tk_offsets tko, s32 clockid) { - if (!do_init) { - if (basetime =3D=3D gact->param.tcfg_basetime && - tko =3D=3D gact->tk_offset && - clockid =3D=3D gact->param.tcfg_clockid) - return; - - spin_unlock_bh(&gact->tcf_lock); - hrtimer_cancel(&gact->hitimer); - spin_lock_bh(&gact->tcf_lock); - } - gact->param.tcfg_basetime =3D basetime; - gact->param.tcfg_clockid =3D clockid; gact->tk_offset =3D tko; - hrtimer_setup(&gact->hitimer, gate_timer_func, clockid, HRTIMER_MODE_ABS_= SOFT); + hrtimer_setup(&gact->hitimer, gate_timer_func, clockid, + HRTIMER_MODE_ABS_SOFT); } =20 static int tcf_gate_init(struct net *net, struct nlattr *nla, @@ -296,20 +296,26 @@ static int tcf_gate_init(struct net *net, struct nlat= tr *nla, struct netlink_ext_ack *extack) { struct tc_action_net *tn =3D net_generic(net, act_gate_ops.net_id); - enum tk_offsets tk_offset =3D TK_OFFS_TAI; - bool bind =3D flags & TCA_ACT_FLAGS_BIND; struct nlattr *tb[TCA_GATE_MAX + 1]; struct tcf_chain *goto_ch =3D NULL; - u64 cycletime =3D 0, basetime =3D 0; - struct tcf_gate_params *p; - s32 clockid =3D CLOCK_TAI; + struct tcf_gate_params *p, *oldp; struct tcf_gate *gact; struct tc_gate *parm; - int ret =3D 0, err; - u32 gflags =3D 0; - s32 prio =3D -1; + struct tcf_gate_params newp =3D { }; ktime_t start; + u64 cycletime =3D 0, basetime =3D 0, cycletime_ext =3D 0; + enum tk_offsets tk_offset =3D TK_OFFS_TAI; + s32 clockid =3D CLOCK_TAI; + u32 gflags =3D 0; u32 index; + s32 prio =3D -1; + bool bind =3D flags & TCA_ACT_FLAGS_BIND; + bool clockid_set =3D false; + bool setup_timer =3D false; + bool update_timer =3D false; + int ret =3D 0, err; + + INIT_LIST_HEAD(&newp.entries); =20 if (!nla) return -EINVAL; @@ -323,6 +329,7 @@ static int tcf_gate_init(struct net *net, struct nlattr= *nla, =20 if (tb[TCA_GATE_CLOCKID]) { clockid =3D nla_get_s32(tb[TCA_GATE_CLOCKID]); + clockid_set =3D true; switch (clockid) { case CLOCK_REALTIME: tk_offset =3D TK_OFFS_REAL; @@ -349,9 +356,6 @@ static int tcf_gate_init(struct net *net, struct nlattr= *nla, if (err < 0) return err; =20 - if (err && bind) - return ACT_P_BOUND; - if (!err) { ret =3D tcf_idr_create_from_flags(tn, index, est, a, &act_gate_ops, bind, flags); @@ -361,94 +365,245 @@ static int tcf_gate_init(struct net *net, struct nla= ttr *nla, } =20 ret =3D ACT_P_CREATED; - } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) { - tcf_idr_release(*a, bind); - return -EEXIST; + gact =3D to_gate(*a); + } else { + if (bind) + return ACT_P_BOUND; + + if (!(flags & TCA_ACT_FLAGS_REPLACE)) { + tcf_idr_release(*a, bind); + return -EEXIST; + } + gact =3D to_gate(*a); } =20 + if (ret !=3D ACT_P_CREATED) + oldp =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->common.tcfa_lock) || + lockdep_is_held(&gact->tcf_lock) || + lockdep_rtnl_is_held()); + else + oldp =3D NULL; + if (tb[TCA_GATE_PRIORITY]) prio =3D nla_get_s32(tb[TCA_GATE_PRIORITY]); + else if (ret !=3D ACT_P_CREATED) + prio =3D oldp->tcfg_priority; =20 - if (tb[TCA_GATE_BASE_TIME]) + if (tb[TCA_GATE_BASE_TIME]) { basetime =3D nla_get_u64(tb[TCA_GATE_BASE_TIME]); + if (basetime > (u64)S64_MAX) { + NL_SET_ERR_MSG(extack, "Base time out of range"); + err =3D -EINVAL; + goto release_idr; + } + } else if (ret !=3D ACT_P_CREATED) { + basetime =3D oldp->tcfg_basetime; + } =20 if (tb[TCA_GATE_FLAGS]) gflags =3D nla_get_u32(tb[TCA_GATE_FLAGS]); - - gact =3D to_gate(*a); - if (ret =3D=3D ACT_P_CREATED) - INIT_LIST_HEAD(&gact->param.entries); + else if (ret !=3D ACT_P_CREATED) + gflags =3D oldp->tcfg_flags; =20 err =3D tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); if (err < 0) goto release_idr; =20 - spin_lock_bh(&gact->tcf_lock); - p =3D &gact->param; + if (!clockid_set) { + if (ret !=3D ACT_P_CREATED) + clockid =3D oldp->tcfg_clockid; + else + clockid =3D CLOCK_TAI; + switch (clockid) { + case CLOCK_REALTIME: + tk_offset =3D TK_OFFS_REAL; + break; + case CLOCK_MONOTONIC: + tk_offset =3D TK_OFFS_MAX; + break; + case CLOCK_BOOTTIME: + tk_offset =3D TK_OFFS_BOOT; + break; + case CLOCK_TAI: + tk_offset =3D TK_OFFS_TAI; + break; + default: + NL_SET_ERR_MSG(extack, "Invalid 'clockid'"); + err =3D -EINVAL; + goto put_chain; + } + } =20 - if (tb[TCA_GATE_CYCLE_TIME]) + if (ret =3D=3D ACT_P_CREATED) + update_timer =3D true; + else if (basetime !=3D oldp->tcfg_basetime || + tk_offset !=3D gact->tk_offset || + clockid !=3D oldp->tcfg_clockid) + update_timer =3D true; + + if (ret =3D=3D ACT_P_CREATED) + setup_timer =3D true; + else if (clockid !=3D oldp->tcfg_clockid) + setup_timer =3D true; + + if (tb[TCA_GATE_CYCLE_TIME]) { cycletime =3D nla_get_u64(tb[TCA_GATE_CYCLE_TIME]); + if (cycletime > (u64)S64_MAX) { + NL_SET_ERR_MSG(extack, "Cycle time out of range"); + err =3D -EINVAL; + goto put_chain; + } + } =20 if (tb[TCA_GATE_ENTRY_LIST]) { - err =3D parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack); - if (err < 0) - goto chain_put; + err =3D parse_gate_list(tb[TCA_GATE_ENTRY_LIST], &newp, extack); + if (err <=3D 0) { + if (!err) + NL_SET_ERR_MSG(extack, + "Missing gate schedule (entry list)"); + err =3D -EINVAL; + goto put_chain; + } + newp.num_entries =3D err; + } else if (ret =3D=3D ACT_P_CREATED) { + NL_SET_ERR_MSG(extack, "Missing schedule entry list"); + err =3D -EINVAL; + goto put_chain; } =20 + if (tb[TCA_GATE_CYCLE_TIME_EXT]) + cycletime_ext =3D nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); + else if (ret !=3D ACT_P_CREATED) + cycletime_ext =3D oldp->tcfg_cycletime_ext; + if (!cycletime) { struct tcfg_gate_entry *entry; - ktime_t cycle =3D 0; + struct list_head *entries; + u64 cycle =3D 0; + + if (!list_empty(&newp.entries)) + entries =3D &newp.entries; + else if (ret !=3D ACT_P_CREATED) + entries =3D &oldp->entries; + else + entries =3D NULL; + + if (!entries) { + NL_SET_ERR_MSG(extack, "Invalid cycle time"); + err =3D -EINVAL; + goto release_new_entries; + } + + list_for_each_entry(entry, entries, list) { + if (entry->interval > (u64)S64_MAX) { + NL_SET_ERR_MSG(extack, + "Cycle time out of range"); + err =3D -EINVAL; + goto release_new_entries; + } + if (cycle > (u64)S64_MAX - entry->interval) { + NL_SET_ERR_MSG(extack, + "Cycle time out of range"); + err =3D -EINVAL; + goto release_new_entries; + } + cycle +=3D entry->interval; + } =20 - list_for_each_entry(entry, &p->entries, list) - cycle =3D ktime_add_ns(cycle, entry->interval); cycletime =3D cycle; if (!cycletime) { + NL_SET_ERR_MSG(extack, "Invalid cycle time"); err =3D -EINVAL; - goto chain_put; + goto release_new_entries; } } - p->tcfg_cycletime =3D cycletime; =20 - if (tb[TCA_GATE_CYCLE_TIME_EXT]) - p->tcfg_cycletime_ext =3D - nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); + if (ret !=3D ACT_P_CREATED && + (tb[TCA_GATE_ENTRY_LIST] || tb[TCA_GATE_CYCLE_TIME] || + cycletime !=3D oldp->tcfg_cycletime)) + update_timer =3D true; =20 - gate_setup_timer(gact, basetime, tk_offset, clockid, - ret =3D=3D ACT_P_CREATED); + p =3D kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + err =3D -ENOMEM; + goto release_new_entries; + } + + INIT_LIST_HEAD(&p->entries); p->tcfg_priority =3D prio; + p->tcfg_basetime =3D basetime; + p->tcfg_cycletime =3D cycletime; + p->tcfg_cycletime_ext =3D cycletime_ext; p->tcfg_flags =3D gflags; - gate_get_start_time(gact, &start); + p->tcfg_clockid =3D clockid; + + if (!list_empty(&newp.entries)) { + list_splice_init(&newp.entries, &p->entries); + p->num_entries =3D newp.num_entries; + } else if (ret !=3D ACT_P_CREATED) { + struct tcfg_gate_entry *entry, *ne; + + list_for_each_entry(entry, &oldp->entries, list) { + ne =3D kmemdup(entry, sizeof(*ne), GFP_KERNEL); + if (!ne) { + err =3D -ENOMEM; + goto free_p; + } + INIT_LIST_HEAD(&ne->list); + list_add_tail(&ne->list, &p->entries); + } + p->num_entries =3D oldp->num_entries; + } =20 - gact->current_close_time =3D start; - gact->current_gate_status =3D GATE_ACT_GATE_OPEN | GATE_ACT_PENDING; + if (update_timer && ret !=3D ACT_P_CREATED) + hrtimer_cancel(&gact->hitimer); + + spin_lock_bh(&gact->tcf_lock); + if (setup_timer) + gate_setup_timer(gact, tk_offset, clockid); =20 + gate_get_start_time(gact, p, &start); + gact->current_close_time =3D start; gact->next_entry =3D list_first_entry(&p->entries, struct tcfg_gate_entry, list); + gact->current_entry_octets =3D 0; + gact->current_gate_status =3D GATE_ACT_GATE_OPEN | GATE_ACT_PENDING; =20 goto_ch =3D tcf_action_set_ctrlact(*a, parm->action, goto_ch); =20 gate_start_timer(gact, start); =20 + oldp =3D rcu_replace_pointer(gact->param, p, + lockdep_is_held(&gact->tcf_lock)); + spin_unlock_bh(&gact->tcf_lock); =20 + if (oldp) + call_rcu(&oldp->rcu, tcf_gate_params_release); + if (goto_ch) tcf_chain_put_by_act(goto_ch); =20 return ret; =20 -chain_put: - spin_unlock_bh(&gact->tcf_lock); - +free_p: + release_entry_list(&p->entries); + kfree(p); +release_new_entries: + release_entry_list(&newp.entries); +put_chain: if (goto_ch) tcf_chain_put_by_act(goto_ch); release_idr: - /* action is not inserted in any list: it's safe to init hitimer - * without taking tcf_lock. - */ - if (ret =3D=3D ACT_P_CREATED) - gate_setup_timer(gact, gact->param.tcfg_basetime, - gact->tk_offset, gact->param.tcfg_clockid, - true); + if (ret =3D=3D ACT_P_CREATED) { + p =3D rcu_dereference_protected(gact->param, 1); + if (p) { + release_entry_list(&p->entries); + kfree(p); + rcu_assign_pointer(gact->param, NULL); + } + } tcf_idr_release(*a, bind); return err; } @@ -458,9 +613,11 @@ static void tcf_gate_cleanup(struct tc_action *a) struct tcf_gate *gact =3D to_gate(a); struct tcf_gate_params *p; =20 - p =3D &gact->param; hrtimer_cancel(&gact->hitimer); - release_entry_list(&p->entries); + + p =3D rcu_dereference_protected(gact->param, 1); + if (p) + call_rcu(&p->rcu, tcf_gate_params_release); } =20 static int dumping_entry(struct sk_buff *skb, @@ -512,7 +669,8 @@ static int tcf_gate_dump(struct sk_buff *skb, struct tc= _action *a, spin_lock_bh(&gact->tcf_lock); opt.action =3D gact->tcf_action; =20 - p =3D &gact->param; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); =20 if (nla_put(skb, TCA_GATE_PARMS, sizeof(opt), &opt)) goto nla_put_failure; --=20 2.52.GIT From nobody Sat Feb 7 17:48:45 2026 Received: from mail-08.mail-europe.com (mail-08.mail-europe.com [57.129.93.249]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B8C891C5D5E; Tue, 20 Jan 2026 00:48:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=57.129.93.249 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768870131; cv=none; b=h8m5j1GG9j+2KGw9iGAOg2DiA7QhGBYY07x5qcYA3ECwRXaexDwflJRL20EkMkZq/YrPylca+e6A8KRQ24vafwfw40l2ICTT4N7qfsAr7OrjApZluOb0vfNtLqMWE+GQePbduPLdzCEVwcqa1zWeRb/UIIjlYDIOwH6ytf/+Sdw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768870131; c=relaxed/simple; bh=/0chezKZEgFOMSPncW/2YTocW8NPuNPAPpLBZ0v6D4U=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=KWmGrMWmrrpF5Gju8uy9M6p6LDdnGS3z9WGS0vqBQNZNuAwTNL7A6udUbfMC4HfS5bBF8e8U0bxvxeFnQN1eLCI4CZJ2opbQ3gmQeFhp6IbZTBH+OFE859S08d+DXZfkGfW35lA5m6iPrt81IwCSbFkfzP+SymB5cNJd9mxrfqU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=1g4.org; spf=pass smtp.mailfrom=1g4.org; dkim=pass (2048-bit key) header.d=1g4.org header.i=@1g4.org header.b=ECnI7NcO; arc=none smtp.client-ip=57.129.93.249 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=1g4.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=1g4.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=1g4.org header.i=@1g4.org header.b="ECnI7NcO" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1g4.org; s=protonmail2; t=1768870119; x=1769129319; bh=hgZfu2gwJF+PAW+NCm5J0Z64EJ32aifzlflRodxQoAE=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=ECnI7NcOhdy92XAVgWGaXTfl/a197ENcuUmQYkjqb6TiMGrnOm8vCZ/PH4ra0t2BC ziT56Pal2F35McA/55kAIJeIgzlHUXrdrp6GsrN5OmJFyH7vPzkXLpjwO+xIjVBSND /KVpZqZtMKQJDszKoQxqTpXe5xUEmOdZO/m1RD37FQ05A+7FReJTgoQ3HdUlTj+7pH fzmQlwykgp7s+em0hvA5s3OmEnesT5zSdhGk/IEivdpyesYjP+ji1PFJh+2WanpZO+ UJVQ/iy3xO4V2ko66yYmFhsRXIzXLXdMxrd17mlkTzT7hzoMr4XcJferCVhFMeJr16 XjQ0CT5/n8E4Q== Date: Tue, 20 Jan 2026 00:48:34 +0000 To: netdev@vger.kernel.org From: Paul Moses Cc: Jamal Hadi Salim , Cong Wang , Jiri Pirko , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-kernel@vger.kernel.org, Paul Moses , stable@vger.kernel.org Subject: [PATCH 2/2] net/sched: act_gate: zero-initialize netlink dump struct Message-ID: <20260120004720.1886632-3-p@1g4.org> In-Reply-To: <20260120004720.1886632-1-p@1g4.org> References: <20260120004720.1886632-1-p@1g4.org> Feedback-ID: 8253658:user:proton X-Pm-Message-ID: 9f9eb4f1232645b510937b51a01122d3023897b6 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Zero-initialize the tc_gate dump struct to avoid leaking padding bytes to userspace. Without clearing the struct, uninitialized stack padding can be copied into the netlink reply during action dumps. Fixes: a51c328df310 ("net: qos: introduce a gate control flow action") Cc: stable@vger.kernel.org Signed-off-by: Paul Moses --- net/sched/act_gate.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index 3ee07c3deaf97..ff963c165de90 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -656,17 +656,16 @@ static int tcf_gate_dump(struct sk_buff *skb, struct = tc_action *a, { unsigned char *b =3D skb_tail_pointer(skb); struct tcf_gate *gact =3D to_gate(a); - struct tc_gate opt =3D { - .index =3D gact->tcf_index, - .refcnt =3D refcount_read(&gact->tcf_refcnt) - ref, - .bindcnt =3D atomic_read(&gact->tcf_bindcnt) - bind, - }; + struct tc_gate opt =3D { }; struct tcfg_gate_entry *entry; struct tcf_gate_params *p; struct nlattr *entry_list; struct tcf_t t; =20 spin_lock_bh(&gact->tcf_lock); + opt.index =3D gact->tcf_index; + opt.refcnt =3D refcount_read(&gact->tcf_refcnt) - ref; + opt.bindcnt =3D atomic_read(&gact->tcf_bindcnt) - bind; opt.action =3D gact->tcf_action; =20 p =3D rcu_dereference_protected(gact->param, --=20 2.52.GIT