Fix potential deadlocks caused by epoll/poll/select

This commit is contained in:
Tate, Hongliang Tian 2020-03-27 05:40:08 +00:00
parent 1a35188212
commit 6dbdbfdcf0

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