mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 16:10:02 +02:00
net/sched: act_gact, act_police: range check the fallback control action
tcf_action_check_ctrlact() range checks the primary control action: if (!opcode) ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0; TC_ACT_VALUE_MAX is TC_ACT_TRAP, so kernel-internal verdicts above it cannot be set that way. But act_gact and act_police each carry a second, independent control action supplied by user space that never reaches that helper - TCA_GACT_PROB.paction and TCA_POLICE_RESULT. Both only reject TC_ACT_GOTO_CHAIN, so any other value is stored verbatim and returned verbatim from the action. In particular user space can store TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is deliberately not part of the UAPI value range. That verdict tells every caller the action took ownership of the skb, so nobody frees it: sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for it. The result is one leaked sk_buff plus its data buffer per packet traversing the filter, unbounded, for all traffic on the chain including kernel-generated packets. Both are trivially deterministic. act_gact clamps tcfg_pval to >= 1, so with pval = 1 gact_determ() returns the fallback for every packet. act_police has no mandatory rate, so rate = 0 leaves tcfp_mtu = ~0 and tcf_police_mtu_check() always passes. TC_ACT_CONSUMED was added by commit720f22fed8("net: sched: refactor reinsert action"), after both goto-chain guards were written: commit9469f375ab("net/sched: act_gact: disallow 'goto chain' on fallback control action") and commitc08f5ed5d6("net/sched: act_police: disallow 'goto chain' on fallback control action"). Neither guard was widened when the new verdict appeared. Factor the existing range test out of tcf_action_check_ctrlact() as tcf_action_valid() and apply it to both fallbacks. The helper cannot call tcf_action_check_ctrlact() directly because that also allocates a goto_chain, which is exactly what these two sites must not do. Reproduced on v7.2-rc6: kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet. With this patch both configurations are rejected with -EINVAL and kmemleak reports none. Fixes:720f22fed8("net: sched: refactor reinsert action") Cc: stable@vger.kernel.org # v5.3+ Signed-off-by: Hyunjung Ko <hj351016@gmail.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Tested-by: Victor Nogueira <victor@mojatatu.com> Link: https://patch.msgid.link/20260806101252.809593-1-hj351016@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
60db47f02b
commit
883b56ae58
|
|
@ -270,6 +270,25 @@ int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
|
|||
struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
|
||||
struct tcf_chain *newchain);
|
||||
|
||||
/* Range check for a control action supplied by user space.
|
||||
*
|
||||
* This is the same test tcf_action_check_ctrlact() applies to the primary
|
||||
* control action, factored out for the *fallback* control actions
|
||||
* (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT),
|
||||
* which must not reach tcf_action_check_ctrlact() because they have no
|
||||
* goto_chain to allocate. Without it, user space can store kernel-internal
|
||||
* verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is
|
||||
* deliberately not part of the UAPI value range.
|
||||
*/
|
||||
static inline bool tcf_action_valid(int action)
|
||||
{
|
||||
int opcode = TC_ACT_EXT_OPCODE(action);
|
||||
|
||||
if (!opcode)
|
||||
return action <= TC_ACT_VALUE_MAX;
|
||||
return opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INET
|
||||
DECLARE_STATIC_KEY_FALSE(tcf_frag_xmit_count);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
|
|||
p_parm = nla_data(tb[TCA_GACT_PROB]);
|
||||
if (p_parm->ptype >= MAX_RAND)
|
||||
return -EINVAL;
|
||||
if (!tcf_action_valid(p_parm->paction)) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"invalid fallback control action");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (TC_ACT_EXT_CMP(p_parm->paction, TC_ACT_GOTO_CHAIN)) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"goto chain not allowed on fallback");
|
||||
|
|
|
|||
|
|
@ -128,6 +128,12 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
|
|||
|
||||
if (tb[TCA_POLICE_RESULT]) {
|
||||
tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]);
|
||||
if (!tcf_action_valid(tcfp_result)) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"invalid fallback control action");
|
||||
err = -EINVAL;
|
||||
goto failure;
|
||||
}
|
||||
if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"goto chain not allowed on fallback");
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user