Component: src/fdtables (dashmaparrayglobal.rs:197, dashmapvecglobal.rs:199)
Severity: Correctness (a race between concurrent dup2() calls)
Found by: CONC-003 (#1304), fdtables::tests::conc_003_dup2_overwrite_refcount_conservation
Symptom
Two dup2()s racing onto the same target fd (or a dup2() racing a close() /
allocation that lands on the same slot) can both observe the same "old" entry and
both decrement it, which double-releases a single host fd. Alternatively, one call's
write can overwrite a concurrent get_unused_virtual_fd()'s insert, losing an entry
entirely.
Root cause
// src/fdtables/src/dashmaparrayglobal.rs (in get_specific_virtual_fd)
let myoptionentry = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize];
// ... guard released here ...
FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry);
The old value is read under one guard and the new value written under a second,
separately acquired one.
Proposed fix
Single .replace() under one guard, dropped before the close handler runs (a handler
may re-enter this module):
- let myoptionentry = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize];
- // always add the new entry. I'm doing this first, before I close
- // the old one because I need to ensure I've cleaned up state correctly
- // before calling the close handlers...
- FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry);
+ let myoptionentry = {
+ let mut row = FDTABLE.get_mut(&cageid).unwrap();
+ row[requested_virtualfd as usize].replace(myentry)
+ };
vanillaglobal.rs and muthashmaxglobal.rs use HashMap::insert, which already
returns the old value atomically, and need no change.
Un-skip on merge
In src/fdtables/src/lib.rs, remove the #[ignore] from
conc_003_dup2_overwrite_refcount_conservation.
Component:
src/fdtables(dashmaparrayglobal.rs:197,dashmapvecglobal.rs:199)Severity: Correctness (a race between concurrent
dup2()calls)Found by: CONC-003 (#1304),
fdtables::tests::conc_003_dup2_overwrite_refcount_conservationSymptom
Two
dup2()s racing onto the same target fd (or adup2()racing aclose()/allocation that lands on the same slot) can both observe the same "old" entry and
both decrement it, which double-releases a single host fd. Alternatively, one call's
write can overwrite a concurrent
get_unused_virtual_fd()'s insert, losing an entryentirely.
Root cause
The old value is read under one guard and the new value written under a second,
separately acquired one.
Proposed fix
Single
.replace()under one guard, dropped before the close handler runs (a handlermay re-enter this module):
vanillaglobal.rsandmuthashmaxglobal.rsuseHashMap::insert, which alreadyreturns the old value atomically, and need no change.
Un-skip on merge
In
src/fdtables/src/lib.rs, remove the#[ignore]fromconc_003_dup2_overwrite_refcount_conservation.