Fix potential deadlocks caused by epoll/poll/select
This commit is contained in:
parent
1a35188212
commit
6dbdbfdcf0
@ -22,10 +22,10 @@ pub fn do_select(
|
||||
|
||||
let current_ref = process::get_current();
|
||||
let mut proc = current_ref.lock().unwrap();
|
||||
let file_table_ref = proc.get_files().lock().unwrap();
|
||||
let file_table = proc.get_files().lock().unwrap();
|
||||
|
||||
for fd in 0..nfds {
|
||||
let fd_ref = file_table_ref.get(fd as FileDesc)?;
|
||||
let fd_ref = file_table.get(fd as FileDesc)?;
|
||||
let (r, w, e) = (
|
||||
readfds.is_set(fd),
|
||||
writefds.is_set(fd),
|
||||
@ -83,6 +83,9 @@ pub fn do_select(
|
||||
revents: 0,
|
||||
});
|
||||
}
|
||||
// Unlock the current process and its file table as early as possible
|
||||
drop(file_table);
|
||||
drop(proc);
|
||||
|
||||
let timeout = match timeout {
|
||||
None => -1,
|
||||
@ -123,12 +126,11 @@ pub fn do_poll(pollfds: &mut [libc::pollfd], timeout: c_int) -> Result<usize> {
|
||||
timeout
|
||||
);
|
||||
|
||||
let current_ref = process::get_current();
|
||||
let mut proc = current_ref.lock().unwrap();
|
||||
|
||||
// Untrusted pollfd's that will be modified by OCall
|
||||
let mut u_pollfds: Vec<libc::pollfd> = pollfds.to_vec();
|
||||
|
||||
let current_ref = process::get_current();
|
||||
let mut proc = current_ref.lock().unwrap();
|
||||
for (i, pollfd) in pollfds.iter_mut().enumerate() {
|
||||
let file_ref = proc
|
||||
.get_files()
|
||||
@ -167,6 +169,8 @@ pub fn do_poll(pollfds: &mut [libc::pollfd], timeout: c_int) -> Result<usize> {
|
||||
return_errno!(EBADF, "not a supported file type");
|
||||
}
|
||||
}
|
||||
// Unlock the current process as early as possible
|
||||
drop(proc);
|
||||
|
||||
let num_events = try_libc!(libc::ocall::poll(
|
||||
u_pollfds.as_mut_ptr(),
|
||||
@ -193,15 +197,8 @@ pub fn do_epoll_create1(flags: c_int) -> Result<FileDesc> {
|
||||
|
||||
let epoll = EpollFile::new()?;
|
||||
let file_ref: Arc<Box<dyn File>> = Arc::new(Box::new(epoll));
|
||||
let current_ref = process::get_current();
|
||||
let mut proc = current_ref.lock().unwrap();
|
||||
let fd = {
|
||||
let close_on_spawn = flags & libc::EPOLL_CLOEXEC != 0;
|
||||
proc.get_files()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.put(file_ref, close_on_spawn)
|
||||
};
|
||||
let fd = process::put_file(file_ref, close_on_spawn)?;
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
@ -213,25 +210,25 @@ pub fn do_epoll_ctl(
|
||||
) -> Result<()> {
|
||||
debug!("epoll_ctl: epfd: {}, op: {:?}, fd: {}", epfd, op, fd);
|
||||
|
||||
let current_ref = process::get_current();
|
||||
let mut proc = current_ref.lock().unwrap();
|
||||
let mut file_table_ref = proc.get_files().lock().unwrap();
|
||||
let mut file_ref = file_table_ref.get(epfd)?;
|
||||
let mut epoll = file_ref.as_epoll()?.inner.lock().unwrap();
|
||||
|
||||
let fd_ref = file_table_ref.get(fd)?;
|
||||
|
||||
let host_fd = if let Ok(socket) = fd_ref.as_socket() {
|
||||
let host_fd = {
|
||||
let fd_ref = process::get_file(fd)?;
|
||||
if let Ok(socket) = fd_ref.as_socket() {
|
||||
socket.fd() as FileDesc
|
||||
} else if let Ok(eventfd) = fd_ref.as_event() {
|
||||
eventfd.get_host_fd() as FileDesc
|
||||
} else {
|
||||
warn!("unsupported file type");
|
||||
return Ok(());
|
||||
return_errno!(EPERM, "unsupported file type");
|
||||
}
|
||||
// Notes on deadlock.
|
||||
//
|
||||
// All locks on fd (if any) will be released at this point. This means
|
||||
// we don't have to worry about the potential deadlock caused by
|
||||
// locking two files (say, fd and epfd) in an inconsistent order.
|
||||
};
|
||||
|
||||
let epoll_file_ref = process::get_file(epfd)?;
|
||||
let epoll = &epoll_file_ref.as_epoll()?.inner;
|
||||
epoll.ctl(op, host_fd, event)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -247,11 +244,8 @@ pub fn do_epoll_wait(
|
||||
timeout
|
||||
);
|
||||
|
||||
let current_ref = process::get_current();
|
||||
let mut proc = current_ref.lock().unwrap();
|
||||
let mut file_ref = proc.get_files().lock().unwrap().get(epfd)?;
|
||||
let mut epoll = file_ref.as_epoll()?.inner.lock().unwrap();
|
||||
|
||||
let epoll_file_ref = process::get_file(epfd)?;
|
||||
let epoll = &epoll_file_ref.as_epoll()?.inner;
|
||||
let count = epoll.wait(events, timeout)?;
|
||||
Ok(count)
|
||||
}
|
||||
@ -284,13 +278,13 @@ impl FdSetExt for libc::fd_set {
|
||||
}
|
||||
|
||||
pub struct EpollFile {
|
||||
inner: SgxMutex<EpollFileInner>,
|
||||
inner: EpollFileInner,
|
||||
}
|
||||
|
||||
impl EpollFile {
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: SgxMutex::new(EpollFileInner::new()?),
|
||||
inner: EpollFileInner::new()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -307,13 +301,8 @@ impl EpollFileInner {
|
||||
Ok(EpollFileInner { epoll_fd: ret })
|
||||
}
|
||||
|
||||
pub fn ctl(
|
||||
&mut self,
|
||||
op: c_int,
|
||||
host_fd: FileDesc,
|
||||
event: *const libc::epoll_event,
|
||||
) -> Result<()> {
|
||||
let ret = try_libc!(libc::ocall::epoll_ctl(
|
||||
pub fn ctl(&self, op: c_int, host_fd: FileDesc, event: *const libc::epoll_event) -> Result<()> {
|
||||
try_libc!(libc::ocall::epoll_ctl(
|
||||
self.epoll_fd,
|
||||
op,
|
||||
host_fd as c_int,
|
||||
@ -324,7 +313,7 @@ impl EpollFileInner {
|
||||
|
||||
/// Wait for an I/O event on the epoll.
|
||||
/// Returns the number of file descriptors ready for the requested I/O.
|
||||
pub fn wait(&mut self, events: &mut [libc::epoll_event], timeout: c_int) -> Result<usize> {
|
||||
pub fn wait(&self, events: &mut [libc::epoll_event], timeout: c_int) -> Result<usize> {
|
||||
let ret = try_libc!(libc::ocall::epoll_wait(
|
||||
self.epoll_fd,
|
||||
events.as_mut_ptr(),
|
||||
@ -351,7 +340,7 @@ impl File for EpollFile {
|
||||
|
||||
impl Debug for EpollFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
let inner = &self.inner;
|
||||
f.debug_struct("EpollFile")
|
||||
.field("epoll_fd", &inner.epoll_fd)
|
||||
.finish()
|
||||
|
Loading…
Reference in New Issue
Block a user