- Renamed linux-0001-audit-filter-inodes → linux-0002 (matches patch file reality) - Renamed linux-0002-dev-alloc-name → linux-0003 - Renamed linux-0003-neigh-parms → linux-0004 - Added linux-0001-headerdep-hash.md (scripts/headerdep.pl detect_cycles CWE-407) - Added linux-0005-component-find-quadratic.md (drivers/base/component.c) - Added linux-0006-btf-module-scan-hash.md (kernel/bpf/btf.c bpf_find_btf_id) - Added linux-0007-pktgen-thread-dev-xarray.md (net/core/pktgen.c) - Added linux-0008-taskstats-listener-hashset.md (kernel/taskstats.c)
3.5 KiB
linux-0003: __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
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 vethin a pod namespace — creates two interfaces, both calldev_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:
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