firefox, go-ethereum, imagemagick, influxdb, micronaut-core, openbsd, proton, proxysql, sqlite, vim. All CWE-407.
4.9 KiB
Proton — CWE-407 Disclosure Brief
2026-04-14 · Patch available — awaiting upstream merge
Finding
Two defects in Valve's Proton compatibility layer: one O(N) linear strcmp scan in interface constructor lookup and one correctness + CWE-407 defect in the prefix migration directory tracker. Both patched. Patches ready for upstream review. The interface lookup defect fires during every game launch; the directory tracker defect causes both incorrect directory skipping and quadratic growth.
The Defects
proton-0001 (PATCHED — MEDIUM): lsteamclient/steamclient_generated.c
// find_iface_constructor — linear scan through 213-entry table:
for (i = 0; i < ARRAYSIZE(constructors); ++i)
if (!strcmp(iface_version, constructors[i].iface_version)) // O(C) where C=213
return constructors[i].ctor;
find_iface_constructor() performs a linear strcmp scan through a 213-entry constructors[] table for every Steam API interface creation request. Called from create_win_interface() during game initialization (10-30 calls per launch). The table entries already appear in alphabetical order in the generated code.
proton-0002 (PATCHED — MEDIUM): proton (Python launch script, line 148)
# merge_user_dir — list += string iterates characters, not paths:
extant_dirs = []
# ...
extant_dirs += dst_dir # BUG: iterates each CHARACTER of dst_dir
extant_dirs += dst_dir on a list with a string iterates the string, adding each CHARACTER as a separate list element instead of the whole path. This creates both a correctness defect (substring check if dir_ in dst_dir on single chars always matches any char present in the path, causing premature directory skipping) AND a CWE-407 defect: the list grows by O(P) elements per directory (P=path length, ~60 chars), and each subsequent directory scans all accumulated characters. With D directories: O(D x D x P) character comparisons.
Complexity Proof
proton-0001: At C=213 interface versions, 20 lookups per game launch:
- Defective: 20 x 213 / 2 = ~2,130 strcmp calls (average case)
- Fixed: 20 x 8 = 160 strcmp calls (binary search, log₂(213) = ~8)
- 13x op reduction per game launch.
proton-0002: At D=100 directories, P=60 chars average path length:
- Defective: list grows to 100 x 60 = 6,000 single-char entries; each new directory scans all: O(D x D x P) = 360,000 character comparisons
- Fixed: set of 100 full paths; each check O(1): O(D) total
- 3,600x op reduction at D=100. Also fixes the correctness defect.
Impact
Proton runs on every Steam Deck and every Linux Steam installation worldwide. The interface lookup defect (proton-0001) fires during game initialization when Steam API interfaces get created. The prefix migration defect (proton-0002) fires during merge_user_dir, which runs when migrating Windows prefix directories during game launch. Beyond performance, proton-0002 causes incorrect behavior: single-character entries in extant_dirs mean any directory whose path contains a common character (like / or e) gets incorrectly skipped during migration, potentially losing save data or configuration.
The Fix
proton-0001: Replace linear scan with binary search (table already sorted alphabetically):
// Before
for (i = 0; i < ARRAYSIZE(constructors); ++i)
if (!strcmp(iface_version, constructors[i].iface_version))
return constructors[i].ctor;
// After
// CWE-407 fix: binary search on sorted table, O(log C) instead of O(C).
int lo = 0, hi = ARRAYSIZE(constructors) - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
int cmp = strcmp(iface_version, constructors[mid].iface_version);
if (cmp == 0) return constructors[mid].ctor;
if (cmp < 0) hi = mid - 1; else lo = mid + 1;
}
proton-0002: Use set.add() instead of list += string; use startswith() for prefix checking:
# Before
extant_dirs = []
extant_dirs += dst_dir # BUG: iterates characters
if dir_ in dst_dir: # checks single-char membership
# After
# CWE-407 fix: set for O(1) membership; append path, not characters.
extant_dirs = set()
extant_dirs.add(dst_dir) # adds whole path
if dst_dir.startswith(extant): # correct prefix check
Patch
defects/proton/patch/proton-0001-find_iface_constructor-linear-strcmp-scan.patch
defects/proton/patch/proton-0002-merge_user_dir-extant_dirs-list-explosion.patch
Unit tests: pass. proton-0001: 13x speedup at C=213. proton-0002: 3,600x speedup at D=100 + correctness fix.
What We Ask
- Confirm receipt and assign a GitHub issue reference (ValveSoftware/Proton).
- Validate patches against your integration test suite, especially prefix migration.
- Assess severity: proton-0002 has a correctness defect alongside the performance issue (characters added instead of paths, causing premature directory skipping).
- Coordinate a disclosure date: we target 90 days from first contact.
Contact: see cover email. This brief is confidential until coordinated disclosure.