From 150167ea4d7abdc3dba504abbbea699c9abeb425 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 13 Oct 2026 04:54:29 -0400 Subject: [PATCH] net/sched: act_gate: cap the number of scheduling entries parse_gate_list() accepts _any number_ of TCA_GATE_ONE_ENTRY elements. The only bound/cap is the enclosing nlattr u16 nla_len (~5460 minimal 12-byte entries). Each entry is a separate GFP_ATOMIC allocation, and the dump of a large list grows one action's reply well past NLMSG_GOODSIZE. The bugs: 1. A single gate can make tcf_dump_walker() silently truncate "tc actions ls" ("net/sched: act_api: report an oversized action dump instead of silently truncating it" makes such an action report -EMSGSIZE instead of dropping the rest of the list). 2. Lack of capping pins memory for as long as the action lives: the largest representable minimal list is roughly 5460 entries, each a separate GFP_ATOMIC allocation, with a per-entry offload array on top in full offload. The held-reply RTM_GETACTION amplification demonstrated in the original posting of this series (Link below) is a generic netlink property this cap does not remove; the bound added here is the install-side pin. So we need to provide an upper bound cap of TCA_GATE_ONE_ENTRY elements. Some context: The gate action offloads through FLOW_ACTION_GATE. The offload interface passes the priority, base time, cycle time, cycle-time extension, the complete entry count and an array holding each entry's gate state, interval, IPV and maximum-octet value. The in-tree SJA1105 DSA driver forwards that complete list to sja1105_vl_gate() without any entry cap check, and composes every supplied entry into the hardware schedule table. Act gate challenges: SJA1105 and SJA1110 are two device families in the one sja1105 driver. They expose different schedule-table capacities: SJA1105 provides up to 1024 schedule entries and SJA1110 up to 4096. These are total table capacities shared with other schedules, so they are upper bounds, not a guaranteed per-action budget. Software gate actions with _a lot more_ entries install and serialize today, so a low cap would reject working configurations and would look like a UAPI breakage; but an infinite size of entries is a bogus choice; a 65-entry configuration was verified during this work to install and dump well-formed. Netlink Challenges: Netlink attributes record their total size, including the four-byte header, in a 16-bit nla_len, so a nested attribute can describe at most 65532 bytes. A minimal accepted input entry is 12 bytes, which puts the max we can fit at roughly 5460 entries; a dumped entry occupies 36 bytes, or 40 bytes when it carries the gate-open flag. 1024 entries therefore serialize to 36868-40964 bytes, which fits inside one nested attribute, while a 4096-entry schedule would need roughly 144-160 KiB which _cannot fit_ at the moment due to the 16bit length. Note: This represents challenges primarily with netlink. The Cap: Cap the list at 1024 entries. This value is based on the existing SJA1105 schedule-table capacity and preserves the verified 65-entry configuration while providing a substantial headroom, and keeps the complete gate list inside the current 16-bit TLV format. It deliberately declines to expose SJA1110's 1025-4096 range through an interface that cannot serialize it, and deliberately rejects software-only schedules above 1024. Why 1024 despite the dump budget: 1024 is a SJA1105 schedule-table capacity, not a dump-budget value. The kernel clamps a dump skb to SKB_WITH_OVERHEAD(32768), so only ~800-890 entries of this list fit "tc actions ls"; the top of the admitted band therefore installs but is not listed. That is accepted deliberately: a unicast RTM_GETACTION reply is pre-sized from the action's advertised size (budgeted per entry since the parent fix), so "tc actions get" enumerates the whole band, and "net/sched: act_api: report an oversized action dump instead of silently truncating it" makes the list view fail loudly with -EMSGSIZE and an extack rather than silently truncating. A filter whose action list contains such a gate also stops "tc filter show" for that chain with -EMSGSIZE (loud), through tc_dump_tfilter()'s no-progress path. This is an operational cap on a configuration the uAPI accepted before, so it is net-next hardening; if we made this a stable backport then it would start rejecting gate configurations that install today (even though those settings would be totally bogus). Conditions to recreate the bug: CAP_NET_ADMIN with CONFIG_NET_ACT_GATE=y. Install a gate action with 1025 or more minimal TCA_GATE_ONE_ENTRY elements over a raw netlink RTM_NEWACTION request. The cap rejects the list with -E2BIG and an extack naming the limit; 1024 entries install and an indexed RTM_GETACTION returns a well-formed reply. Link: https://lore.kernel.org/netdev/QDISC-H19Z.v1.20261001053234@mojatatu.com/ Reviewed-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/QDISC-H19Z.v3.r0.20261012193813-3@mojatatu.com Signed-off-by: Jakub Kicinski --- net/sched/act_gate.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index 14801c604bd9..711cf2443540 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -18,6 +18,13 @@ static struct tc_action_ops act_gate_ops; +/* A nested attribute's u16 nla_len caps it at 65532 bytes. tcf_gate_dump() + * emits each entry as 36 bytes (40 with the gate-open flag), so 4096 entries + * would need ~144-160 KiB and cannot be represented. 1024 matches the + * SJA1105 schedule-table capacity (SJA1110 provides 4096). + */ +#define GATE_ENTRIES_MAX 1024U + static ktime_t gate_get_time(struct tcf_gate *gact) { ktime_t mono = ktime_get(); @@ -277,6 +284,14 @@ static int parse_gate_list(struct nlattr *list_attr, continue; } + if (i >= GATE_ENTRIES_MAX) { + NL_SET_ERR_MSG_FMT(extack, + "Too many schedule entries, at most %u are supported", + GATE_ENTRIES_MAX); + err = -E2BIG; + goto release_list; + } + entry = kzalloc_obj(*entry, GFP_ATOMIC); if (!entry) { NL_SET_ERR_MSG(extack, "Not enough memory for entry");