ConfirmedCWE-20 — Improper Input Validation
merge_cpusets returns Err when one operand is empty — empty string split yields unparsable empty element
View Upstream Issuegithub.com/kuasar-io/kuasar/issues/245CWE:CWE-20 — Improper Input Validation
Repository:kuasar
Date:2026-05-19
Affected Version:*
Component:vmm/sandbox
Reporter:Toan
Affected Files
vmm/sandbox/src/utils.rsSummary
merge_cpusets in vmm/sandbox/src/utils.rs:197 returns Err when either operand is an empty string. An empty cpuset means "no CPU affinity specified" and should be valid. The root cause is that "".split(',') yields [""] (one empty-string element), which fails to parse as u32.
Vulnerable Code
// vmm/sandbox/src/utils.rs:197
fn merge_cpusets(cpuset1: &str, cpuset2: &str) -> Result<String> {
// ...
// When cpuset1 or cpuset2 is "", the split produces [""]
// which fails to parse as u32, returning Err
}
Reproduction
merge_cpusets("0", "") // Returns Err, should return Ok("0")
merge_cpusets("", "0") // Returns Err, should return Ok("0")
A non-empty cpuset merged with an empty one should yield the non-empty cpuset. Instead, the function errors out.
Root Cause
"".split(',') returns an iterator producing [""] — a single empty-string element. When the function attempts to parse this element as a u32, it fails, causing the entire merge to fail.
Trigger Conditions
- Two containers share resources via
merge_resources(utils.rs:153) - One container has a cpuset specified (e.g.
"0-3"), the other has none (empty string"") merge_cpusetsis called with the empty cpuset as either operand- Empty string split yields
[""], which failsu32parsing →Errreturned - Resource merging fails, container cannot start or receive CPU affinity
Impact
- Container startup failure: Resource merging fails when one container has no CPU affinity, preventing container creation or update
- No workaround: Downstream code does not handle the
Errgracefully in all paths - Intermittent: Only manifests when one side of the merge has an unset cpuset — easily missed in testing
Suggested Fix
Handle empty cpuset strings before splitting and parsing:
fn merge_cpusets(cpuset1: &str, cpuset2: &str) -> Result<String> {
// Treat empty cpuset as "no affinity" — skip it
if cpuset1.is_empty() {
return Ok(cpuset2.to_string());
}
if cpuset2.is_empty() {
return Ok(cpuset1.to_string());
}
// ... existing merge logic ...
}
Alternatively, filter out empty elements after splitting:
let cpus: Vec<u32> = cpuset.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.parse::<u32>())
.collect::<Result<Vec<_>, _>>()?;