Netdev List
 help / color / mirror / Atom feed
* [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations
@ 2026-08-28 12:19 Jamal Hadi Salim
  2026-08-28 12:19 ` [PATCH net 2/2] selftests: tc-testing: add tx_queue_len cap regression tests Jamal Hadi Salim
  2026-09-01  3:08 ` [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations Jakub Kicinski
  0 siblings, 2 replies; 3+ messages in thread
From: Jamal Hadi Salim @ 2026-08-28 12:19 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Willem de Bruijn,
	Jason Wang, Andrew Lunn, stable, vega, Victor Nogueira

Several subsystems allocate ring buffers sized by dev->tx_queue_len
with no upper bound. An unprivileged user (via unshare -Urn) can set a
huge tx_queue_len and create many devices/queues to exhaust global
memory, causing a system-wide OOM:

- pfifo_fast: pfifo_fast_init() and pfifo_fast_change_tx_queue_len()
  allocate 3 skb_array rings of tx_queue_len entries each.
- tun: tun_queue_resize() and the queue-attach path resize ptr_rings
  to tx_queue_len on the NETDEV_CHANGE_TX_QUEUE_LEN notifier.
- tap (macvtap/ipvtap): tap_queue_resize() and tap_init() resize/init
  ptr_rings to tx_queue_len on the same notifier.

netif_change_tx_queue_len() is the single entry point for IFLA_TXQLEN,
sysfs, and the SIOCSIFTXQLEN ioctl. Cap new_len at S16_MAX (32767)
there so the oversized value is rejected at set time. This will take 
effect whether the device is up or down before dev->tx_queue_len is
written or any notifier fires or any ring is allocated. So a good
choke spot.

S16_MAX is the virtio virtqueue size limit: the virtio specification
stores the queue size as a u16 with a maximum of 32768, so 32767 is
the largest tx_queue_len any in-tree driver can meaningfully use.
Values above that only serve to inflate ring allocations.

Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_VETH=y, CONFIG_USER_NS=y, CONFIG_NET_NS=y.
- Unprivileged user in a fresh user+net namespace (unshare -Urn).
- pfifo_fast: create veth pairs, set tx_queue_len to 500000, attach
  mq+pfifo_fast. ~28 iterations OOMs a 2GB guest.
- tun: create 50 tun devices with IFF_MULTI_QUEUE, set tx_queue_len to
  500000, open 8 queues each. ~1.6GB of ptr_ring allocations OOMs a
  512MB guest.
- tap: same as tun with IFF_TAP. ~960MB OOMs a 512MB guest.
- On the fixed kernel the oversized tx_queue_len is rejected with
  -ERANGE at set time.

Fixes: 6a643ddb5624 ("net: introduce helper dev_change_tx_queue_len()")
Reported-by: vega@nebusec.ai
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 net/core/dev.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index 38336858c168..1d3fc0a268a5 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -9982,7 +9982,7 @@ int netif_change_tx_queue_len(struct net_device *dev, unsigned long new_len)
 	unsigned int orig_len = dev->tx_queue_len;
 	int res;
 
-	if (new_len != (unsigned int)new_len)
+	if (new_len > S16_MAX)
 		return -ERANGE;
 
 	if (new_len != orig_len) {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH net 2/2] selftests: tc-testing: add tx_queue_len cap regression tests
  2026-08-28 12:19 [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations Jamal Hadi Salim
@ 2026-08-28 12:19 ` Jamal Hadi Salim
  2026-09-01  3:08 ` [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations Jakub Kicinski
  1 sibling, 0 replies; 3+ messages in thread
From: Jamal Hadi Salim @ 2026-08-28 12:19 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Willem de Bruijn,
	Jason Wang, Andrew Lunn, vega, Victor Nogueira

Add two test cases for the S16_MAX tx_queue_len cap in
netif_change_tx_queue_len():

1. Set path: set txqueuelen to 100000000. On a fixed kernel the
   change is rejected with -ERANGE (exit code 2) and txqueuelen
   stays at the default 1000.

2. Resize path: set txqueuelen to 500, attach pfifo_fast, then raise
   txqueuelen to 100000000. On a fixed kernel the resize is rejected
   with -ERANGE and txqueuelen rolls back to 500.

Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 .../tc-tests/qdiscs/pfifo_fast.json           | 51 ++++++++++++++++---
 1 file changed, 45 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json
index 30da27fe8806..03668d86bd0a 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json
@@ -9,8 +9,7 @@
         "plugins": {
             "requires": "nsPlugin"
         },
-        "setup": [
-        ],
+        "setup": [],
         "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast",
         "expExitCode": "0",
         "verifyCmd": "$TC qdisc show dev $DUMMY",
@@ -30,8 +29,7 @@
         "plugins": {
             "requires": "nsPlugin"
         },
-        "setup": [
-        ],
+        "setup": [],
         "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast",
         "expExitCode": "0",
         "verifyCmd": "$TC -s qdisc show dev $DUMMY",
@@ -81,8 +79,7 @@
         "verifyCmd": "$TC qdisc show dev $DUMMY",
         "matchPattern": "qdisc pfifo_fast 1: root refcnt [0-9]+ bands 3 priomap",
         "matchCount": "0",
-        "teardown": [
-        ]
+        "teardown": []
     },
     {
         "id": "4385",
@@ -105,5 +102,47 @@
         "teardown": [
             "$TC qdisc del dev $DUMMY handle 1: root"
         ]
+    },
+    {
+        "id": "ed33",
+        "name": "Reject tx_queue_len exceeding S16_MAX at set time",
+        "category": [
+            "qdisc",
+            "pfifo_fast"
+        ],
+        "plugins": {
+            "requires": "nsPlugin"
+        },
+        "setup": [],
+        "cmdUnderTest": "$IP link set dev $DUMMY txqueuelen 100000000",
+        "expExitCode": "2",
+        "verifyCmd": "$IP link show dev $DUMMY",
+        "matchPattern": "qlen 1000$",
+        "matchCount": "1",
+        "teardown": []
+    },
+    {
+        "id": "04b5",
+        "name": "Reject tx_queue_len resize exceeding S16_MAX, rollback to original",
+        "category": [
+            "qdisc",
+            "pfifo_fast"
+        ],
+        "plugins": {
+            "requires": "nsPlugin"
+        },
+        "setup": [
+            "$IP link set dev $DUMMY txqueuelen 500",
+            "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast"
+        ],
+        "cmdUnderTest": "$IP link set dev $DUMMY txqueuelen 100000000",
+        "expExitCode": "2",
+        "verifyCmd": "$IP link show dev $DUMMY",
+        "matchPattern": "qlen 500$",
+        "matchCount": "1",
+        "teardown": [
+            "$TC qdisc del dev $DUMMY handle 1: root",
+            "$IP link set dev $DUMMY txqueuelen 1000"
+        ]
     }
 ]
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations
  2026-08-28 12:19 [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations Jamal Hadi Salim
  2026-08-28 12:19 ` [PATCH net 2/2] selftests: tc-testing: add tx_queue_len cap regression tests Jamal Hadi Salim
@ 2026-09-01  3:08 ` Jakub Kicinski
  1 sibling, 0 replies; 3+ messages in thread
From: Jakub Kicinski @ 2026-09-01  3:08 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, Jiri Pirko, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Willem de Bruijn, Jason Wang, Andrew Lunn, stable,
	vega, Victor Nogueira

On Fri, 28 Aug 2026 08:19:01 -0400 Jamal Hadi Salim wrote:
> Several subsystems allocate ring buffers sized by dev->tx_queue_len
> with no upper bound. An unprivileged user (via unshare -Urn) can set a
> huge tx_queue_len and create many devices/queues to exhaust global
> memory, causing a system-wide OOM:
> 
> - pfifo_fast: pfifo_fast_init() and pfifo_fast_change_tx_queue_len()
>   allocate 3 skb_array rings of tx_queue_len entries each.
> - tun: tun_queue_resize() and the queue-attach path resize ptr_rings
>   to tx_queue_len on the NETDEV_CHANGE_TX_QUEUE_LEN notifier.
> - tap (macvtap/ipvtap): tap_queue_resize() and tap_init() resize/init
>   ptr_rings to tx_queue_len on the same notifier.
> 
> netif_change_tx_queue_len() is the single entry point for IFLA_TXQLEN,
> sysfs, and the SIOCSIFTXQLEN ioctl. Cap new_len at S16_MAX (32767)
> there so the oversized value is rejected at set time. This will take 
> effect whether the device is up or down before dev->tx_queue_len is
> written or any notifier fires or any ring is allocated. So a good
> choke spot.
> 
> S16_MAX is the virtio virtqueue size limit: the virtio specification
> stores the queue size as a u16 with a maximum of 32768, so 32767 is
> the largest tx_queue_len any in-tree driver can meaningfully use.
> Values above that only serve to inflate ring allocations.
> 
> Conditions to recreate the bug:
> - CONFIG_NET_SCHED=y, CONFIG_VETH=y, CONFIG_USER_NS=y, CONFIG_NET_NS=y.
> - Unprivileged user in a fresh user+net namespace (unshare -Urn).
> - pfifo_fast: create veth pairs, set tx_queue_len to 500000, attach
>   mq+pfifo_fast. ~28 iterations OOMs a 2GB guest.
> - tun: create 50 tun devices with IFF_MULTI_QUEUE, set tx_queue_len to
>   500000, open 8 queues each. ~1.6GB of ptr_ring allocations OOMs a
>   512MB guest.
> - tap: same as tun with IFF_TAP. ~960MB OOMs a 512MB guest.
> - On the fixed kernel the oversized tx_queue_len is rejected with
>   -ERANGE at set time.
> 
> Fixes: 6a643ddb5624 ("net: introduce helper dev_change_tx_queue_len()")
> Reported-by: vega@nebusec.ai
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
> ---
>  net/core/dev.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 38336858c168..1d3fc0a268a5 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -9982,7 +9982,7 @@ int netif_change_tx_queue_len(struct net_device *dev, unsigned long new_len)
>  	unsigned int orig_len = dev->tx_queue_len;
>  	int res;
>  
> -	if (new_len != (unsigned int)new_len)
> +	if (new_len > S16_MAX)
>  		return -ERANGE;
>  
>  	if (new_len != orig_len) {

clashiko points out that we need to also cover the newlink path:

net/core/rtnetlink.c:rtnl_create_link() {
	...
	if (tb[IFLA_TXQLEN])
		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
	...

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-01  3:08 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-28 12:19 [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations Jamal Hadi Salim
2026-08-28 12:19 ` [PATCH net 2/2] selftests: tc-testing: add tx_queue_len cap regression tests Jamal Hadi Salim
2026-09-01  3:08 ` [PATCH net 1/2] net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations Jakub Kicinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox