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 <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Link: https://patch.msgid.link/QDISC-H19Z.v3.r0.20261012193813-3@mojatatu.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jamal Hadi Salim 2026-10-13 04:54:29 -04:00 committed by Jakub Kicinski
parent 1a49567560
commit 150167ea4d

View File

@ -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");