Add 88 new defect entries to HIGH and MEDIUM tables:
HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002
MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
ovs-0001, onos-0003, odl-0002, jetty-0001
PDF: 976K
94 lines
3.5 KiB
Markdown
94 lines
3.5 KiB
Markdown
# linux-0002: __dev_alloc_name — O(D×A) nested sscanf on every interface rename
|
||
|
||
**File:** `net/core/dev.c`
|
||
**Function:** `__dev_alloc_name()` (line ~1358)
|
||
**Severity:** MEDIUM — triggered on every `ip link add`, `ip link set name`, container veth creation
|
||
**CWE:** CWE-407 (Inefficient Algorithmic Complexity)
|
||
|
||
## Code
|
||
|
||
```c
|
||
static int __dev_alloc_name(struct net *net, const char *name, char *res)
|
||
{
|
||
/* ... */
|
||
for_each_netdev(net, d) { // O(D) — all devices
|
||
struct netdev_name_node *name_node;
|
||
|
||
netdev_for_each_altname(d, name_node) { // O(A) — alt names per device
|
||
if (!sscanf(name_node->name, name, &i)) // string parse each time
|
||
continue;
|
||
if (i < 0 || i >= max_netdevices)
|
||
continue;
|
||
snprintf(buf, IFNAMSIZ, name, i);
|
||
if (!strncmp(buf, name_node->name, IFNAMSIZ))
|
||
__set_bit(i, inuse);
|
||
}
|
||
/* same sscanf/snprintf/strncmp on d->name */
|
||
}
|
||
i = find_first_zero_bit(inuse, max_netdevices);
|
||
/* ... */
|
||
}
|
||
```
|
||
|
||
## Complexity
|
||
|
||
| Variable | Meaning |
|
||
|----------|---------|
|
||
| D | Number of net devices in the namespace |
|
||
| A | Number of alternative names per device |
|
||
|
||
Total work per call: **O(D × A × sscanf_cost)**.
|
||
|
||
`sscanf` with a format containing `%d` is not O(1); it involves format string parsing.
|
||
Container-heavy hosts (Kubernetes nodes) routinely carry D=500+ veth/bridge/vlan
|
||
devices, each with 1-3 alt names from `ip link property add`.
|
||
|
||
## When Triggered
|
||
|
||
- `ip link add vethN type veth` in a pod namespace — creates two interfaces,
|
||
both call `dev_alloc_name()` with format `"veth%d"`.
|
||
- A node creating 100 pods triggers 200 calls; if D=400 devices already exist
|
||
with A=2 alt names, each call walks 800 entries.
|
||
- Batch pod launches show O(D²) total work as D grows.
|
||
|
||
## Root Cause
|
||
|
||
The bitmap approach is correct for the final `find_first_zero_bit`, but the bitmap
|
||
is populated by rescanning all devices + alt names on every call. The primary
|
||
device name already uses the hash (`dev_name_hash`), but alt names bypass the hash
|
||
and fall into the linear `netdev_for_each_altname` walk.
|
||
|
||
## Fix
|
||
|
||
Maintain a sorted or hash-keyed index of all allocated numeric suffixes per name
|
||
prefix. On device/altname registration, insert the suffix into the prefix's free
|
||
map; on deregistration, remove it. `__dev_alloc_name` then does one map lookup
|
||
to find the first free slot in O(log D) or O(1) amortized.
|
||
|
||
Simpler interim fix: skip the `netdev_for_each_altname` inner loop when counting
|
||
in-use slots for the primary name format, since alt names use different formats
|
||
(verified by `netdev_name_node_alt_create` — alt names are explicit strings, not
|
||
`%d` patterns). Add a guard:
|
||
|
||
```c
|
||
netdev_for_each_altname(d, name_node) {
|
||
/* Alt names created via 'ip link property add' are never %d patterns;
|
||
* skip sscanf unless the alt name could plausibly match. */
|
||
if (!memchr(name_node->name, '0' + (i % 10), IFNAMSIZ))
|
||
continue; /* fast reject — not numeric */
|
||
/* ... existing sscanf logic ... */
|
||
}
|
||
```
|
||
|
||
Full fix: maintain per-prefix bitmaps in a global xarray keyed by name prefix hash.
|
||
|
||
## Impact
|
||
|
||
- O(D×A) work per interface creation; D and A both grow with container density.
|
||
- On a Kubernetes node with 500 pods: ~1000 devices × 2 alt names = 2000 sscanf
|
||
calls per new pod creation, versus O(1) with an index.
|
||
- Multiplied by pod churn rate (100/min), this is 200,000 sscanf calls/min.
|
||
|
||
## Patch
|
||
|
||
See `defects/linux/patch/linux-0003-dev-alloc-name-nested-altname.patch`
|