Skip to content

Commit 0c4c82a

Browse files
authored
fix fdtables race conditions and off-by-one boundary check (#1057)
close_virtualfd: mutate in place through the DashMap guard instead of copy-modify-reinsert, which had a window where concurrent fd opens could be silently overwritten. _increment_fdcount: use entry().or_insert(0) instead of get_mut/insert to prevent two threads from both inserting 1 when they race on a missing key. set_specific_virtualfd: change > to >= for FD_PER_PROCESS_MAX bounds check to prevent index 1024 on a [_; 1024] array. Fixes #1051
1 parent a035a53 commit 0c4c82a

File tree

2 files changed

+38
-45
lines changed

2 files changed

+38
-45
lines changed

src/fdtables/src/dashmaparrayglobal.rs

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ pub fn get_specific_virtual_fd(
205205
// Note that, I need to use the FD_PER_PROCESS_MAX setting because this
206206
// is also how I'm tracking how many values you have open. If this
207207
// changed, then these constants could be decoupled...
208-
if requested_virtualfd > FD_PER_PROCESS_MAX {
208+
if requested_virtualfd >= FD_PER_PROCESS_MAX {
209209
return Err(threei::Errno::EBADF as u64);
210210
}
211211

@@ -404,24 +404,23 @@ pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> {
404404

405405
assert!(check_cage_exists(cageid),"Unknown cageid in fdtable access");
406406

407-
// derefing this so I don't hold a lock and deadlock close handlers
408-
let mut myfdrow = *FDTABLE.get_mut(&cageid).unwrap();
409-
410-
411-
if myfdrow[virtfd as usize].is_some() {
412-
let entry = myfdrow[virtfd as usize];
413-
414-
// Zero out this entry before calling the close handler...
415-
myfdrow[virtfd as usize] = None;
407+
// Mutate in place under the guard, extract the entry, then drop the
408+
// guard before calling the close handler (which may re-enter fdtables).
409+
let entry = {
410+
let mut guard = FDTABLE.get_mut(&cageid).unwrap();
411+
let entry = guard[virtfd as usize];
412+
guard[virtfd as usize] = None;
413+
entry
414+
// guard drops here — lock released
415+
};
416416

417-
// Re-insert the modified myfdrow since I've been modifying a copy
418-
FDTABLE.insert(cageid, myfdrow.clone());
419-
420-
// always _decrement last as it may call the user handler...
421-
_decrement_fdcount(entry.unwrap());
422-
return Ok(());
417+
match entry {
418+
Some(e) => {
419+
_decrement_fdcount(e);
420+
Ok(())
421+
}
422+
None => Err(threei::Errno::EBADFD as u64),
423423
}
424-
Err(threei::Errno::EBADFD as u64)
425424
}
426425

427426

@@ -501,12 +500,9 @@ fn _increment_fdcount(entry:FDTableEntry) {
501500

502501
let mytuple = (entry.fdkind, entry.underfd);
503502

504-
// Get a mutable reference to the entry so we can update it.
505-
if let Some(mut count) = FDCOUNT.get_mut(&mytuple) {
506-
*count += 1;
507-
} else {
508-
FDCOUNT.insert(mytuple, 1);
509-
}
503+
// Atomic increment-or-insert via entry API to avoid race where two
504+
// threads both see None and both insert 1.
505+
*FDCOUNT.entry(mytuple).or_insert(0) += 1;
510506
}
511507

512508

src/fdtables/src/dashmapvecglobal.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ pub fn get_specific_virtual_fd(
204204
// Note that, I need to use the FD_PER_PROCESS_MAX setting because this
205205
// is also how I'm tracking how many values you have open. If this
206206
// changed, then these constants could be decoupled...
207-
if requested_virtualfd > FD_PER_PROCESS_MAX {
207+
if requested_virtualfd >= FD_PER_PROCESS_MAX {
208208
return Err(threei::Errno::EBADF as u64);
209209
}
210210

@@ -402,23 +402,23 @@ pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> {
402402

403403
assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access");
404404

405-
// cloning this so I don't hold a lock and deadlock close handlers
406-
let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap().clone();
407-
408-
409-
if myfdrow[virtfd as usize].is_some() {
410-
let entry = myfdrow[virtfd as usize];
411-
412-
// Zero out this entry before calling the close handler...
413-
myfdrow[virtfd as usize] = None;
414-
415-
FDTABLE.insert(cageid, myfdrow.clone());
405+
// Mutate in place under the guard, extract the entry, then drop the
406+
// guard before calling the close handler (which may re-enter fdtables).
407+
let entry = {
408+
let mut guard = FDTABLE.get_mut(&cageid).unwrap();
409+
let entry = guard[virtfd as usize];
410+
guard[virtfd as usize] = None;
411+
entry
412+
// guard drops here — lock released
413+
};
416414

417-
// always _decrement last as it may call the user handler...
418-
_decrement_fdcount(entry.unwrap());
419-
return Ok(());
415+
match entry {
416+
Some(e) => {
417+
_decrement_fdcount(e);
418+
Ok(())
419+
}
420+
None => Err(threei::Errno::EBADFD as u64),
420421
}
421-
Err(threei::Errno::EBADFD as u64)
422422
}
423423

424424

@@ -498,12 +498,9 @@ fn _increment_fdcount(entry:FDTableEntry) {
498498

499499
let mytuple = (entry.fdkind, entry.underfd);
500500

501-
// Get a mutable reference to the entry so we can update it.
502-
if let Some(mut count) = FDCOUNT.get_mut(&mytuple) {
503-
*count += 1;
504-
} else {
505-
FDCOUNT.insert(mytuple, 1);
506-
}
501+
// Atomic increment-or-insert via entry API to avoid race where two
502+
// threads both see None and both insert 1.
503+
*FDCOUNT.entry(mytuple).or_insert(0) += 1;
507504
}
508505

509506

0 commit comments

Comments
 (0)