Skip to content
Snippets Groups Projects
Commit b4303c3f authored by Jacob Lorentzon's avatar Jacob Lorentzon
Browse files

Fix CallRequest unsoundness.

parent fecc5ceb
No related branches found
No related tags found
2 merge requests!6Redesign API to fix (most) soundness and add async support.,!5Draft: Improved async-capable interface
......@@ -26,7 +26,7 @@ pub enum OpenResult {
use core::mem::size_of;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default)]
#[derive(Debug, Default)]
pub struct Request {
sqe: Sqe,
}
......@@ -40,11 +40,13 @@ pub struct CancellationRequest {
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
#[derive(Debug)]
pub struct CallRequest {
inner: Request,
}
#[repr(transparent)]
#[derive(Debug)]
pub struct SendFdRequest {
inner: Request,
}
......@@ -61,15 +63,15 @@ pub enum RequestKind {
impl CallRequest {
#[inline]
pub fn request(&self) -> Request {
self.inner
pub fn request(&self) -> &Request {
&self.inner
}
}
impl SendFdRequest {
#[inline]
pub fn request(&self) -> Request {
self.inner
pub fn request(&self) -> &Request {
&self.inner
}
pub fn id(&self) -> usize {
......@@ -173,7 +175,8 @@ impl Socket {
pub fn next_request(&self, behavior: SignalBehavior) -> Result<Option<Request>> {
let mut buf = [Request::default()];
Ok(if self.read_requests(&mut buf, behavior)? > 0 {
Some(buf[0])
let [req] = buf;
Some(req)
} else {
None
})
......@@ -203,7 +206,7 @@ impl Socket {
pub struct Response(Cqe);
impl Response {
pub fn new(req: &CallRequest, status: Result<usize>) -> Self {
pub fn new(req: CallRequest, status: Result<usize>) -> Self {
Self(Cqe {
flags: CqeOpcode::RespondRegular as u8,
extra_raw: [0_u8; 3],
......@@ -211,16 +214,16 @@ impl Response {
tag: req.inner.sqe.tag,
})
}
pub fn open_dup_like(req: &CallRequest, res: Result<OpenResult>) -> Response {
pub fn open_dup_like(req: CallRequest, res: Result<OpenResult>) -> Response {
match res {
Ok(OpenResult::ThisScheme { number, flags }) => {
Response::new(&req, Ok(number)).with_extra([flags.bits(), 0, 0])
Response::new(req, Ok(number)).with_extra([flags.bits(), 0, 0])
}
Err(e) => Response::new(&req, Err(e)),
Ok(OpenResult::OtherScheme { fd }) => Response::return_external_fd(&req, fd),
Err(e) => Response::new(req, Err(e)),
Ok(OpenResult::OtherScheme { fd }) => Response::return_external_fd(req, fd),
}
}
pub fn return_external_fd(req: &CallRequest, fd: usize) -> Self {
pub fn return_external_fd(req: CallRequest, fd: usize) -> Self {
Self(Cqe {
flags: CqeOpcode::RespondWithFd as u8,
extra_raw: [0_u8; 3],
......@@ -235,7 +238,7 @@ impl Response {
})
}
pub fn for_sendfd(req: &SendFdRequest, status: Result<usize>) -> Self {
pub fn for_sendfd(req: SendFdRequest, status: Result<usize>) -> Self {
Self(Cqe {
flags: CqeOpcode::RespondRegular as u8,
extra_raw: [0_u8; 3],
......
......@@ -112,6 +112,12 @@ pub enum Op<'a> {
}
impl<'a> Op<'a> {
/// Decode the raw SQE into an Op with borrowed buffers passed as slices.
///
/// # Safety
///
/// Any borrowed buffers will be unmapped whenever a response is sent, which unlike the
/// move-based CallRequest API, needs to be managed manually by the caller.
pub unsafe fn from_sqe_unchecked(sqe: &'a Sqe) -> Option<Op<'a>> {
let opcode = Opcode::try_from_raw(sqe.opcode)?;
let args = sqe.args;
......@@ -234,202 +240,180 @@ impl<'a> Op<'a> {
}
}
impl CallRequest {
pub fn with<T>(self, f: impl FnOnce(Self, &CallerCtx, Op<'_>) -> T) -> Result<T, Response> {
pub fn caller(&self) -> CallerCtx {
let sqe = &self.inner.sqe;
let caller = CallerCtx {
CallerCtx {
pid: sqe.caller as usize,
uid: sqe.args[5] as u32,
gid: (sqe.args[5] >> 32) as u32,
};
let Some(op) = (unsafe { Op::from_sqe_unchecked(sqe) }) else {
return Err(Response::new(&self, Err(Error::new(ENOSYS))));
};
Ok(f(self, &caller, op))
}
}
pub fn op(&self) -> Option<Op<'_>> {
unsafe { Op::from_sqe_unchecked(&self.inner.sqe) }
}
pub async fn handle_async(self, s: &mut impl SchemeAsync) -> Result<Response> {
let sqe = &self.inner.sqe;
let caller = self.caller();
let caller = CallerCtx {
pid: sqe.caller as usize,
uid: sqe.args[5] as u32,
gid: (sqe.args[5] >> 32) as u32,
let Some(op) = self.op() else {
return Ok(Response::new(self, Err(Error::new(ENOSYS))));
};
let Some(op) = (unsafe { Op::from_sqe_unchecked(sqe) }) else {
return Ok(Response::new(&self, Err(Error::new(ENOSYS))));
};
let res = match op {
Op::Open { path, flags } => {
let res = s.open(path, flags, &caller).await;
return Ok(Response::open_dup_like(self, res));
}
Op::Rmdir { path } => s.rmdir(path, &caller).await.map(|()| 0),
Op::Unlink { path } => s.unlink(path, &caller).await.map(|()| 0),
Ok(Response::new(
&self,
match op {
Op::Open { path, flags } => {
return Ok(Response::open_dup_like(
&self,
s.open(path, flags, &caller).await,
))
}
Op::Rmdir { path } => s.rmdir(path, &caller).await.map(|()| 0),
Op::Unlink { path } => s.unlink(path, &caller).await.map(|()| 0),
Op::Dup { old_fd, buf } => {
return Ok(Response::open_dup_like(
&self,
s.dup(old_fd, buf, &caller).await,
))
}
Op::Read {
fd,
buf,
offset,
flags,
} => s.read(fd, buf, offset, flags, &caller).await,
Op::Write {
fd,
buf,
offset,
flags,
} => s.write(fd, buf, offset, flags, &caller).await,
// TODO: Don't convert to usize
Op::Fsize { fd } => s.fsize(fd, &caller).await.map(|l| l as usize),
Op::Fchmod { fd, new_mode } => s.fchmod(fd, new_mode, &caller).await.map(|()| 0),
Op::Fchown {
fd,
new_uid,
new_gid,
} => s.fchown(fd, new_uid, new_gid, &caller).await.map(|()| 0),
Op::Fcntl { fd, cmd, arg } => s.fcntl(fd, cmd, arg, &caller).await,
Op::Fevent { fd, req_flags } => {
s.fevent(fd, req_flags, &caller).await.map(|f| f.bits())
}
Op::Fpath { fd, buf } => s.fpath(fd, buf, &caller).await,
Op::Frename { fd, new_path } => s.frename(fd, new_path, &caller).await,
Op::Fstat { fd, stat } => s.fstat(fd, stat, &caller).await.map(|()| 0),
Op::FstatVfs { fd, stat } => s.fstatvfs(fd, stat, &caller).await.map(|()| 0),
Op::Fsync { fd } => s.fsync(fd, &caller).await.map(|()| 0),
Op::Ftruncate { fd, new_sz } => s.ftruncate(fd, new_sz, &caller).await.map(|()| 0),
Op::Futimens { fd, times } => s.futimens(fd, times, &caller).await.map(|()| 0),
Op::MmapPrep {
fd,
offset,
len,
flags,
} => s.mmap_prep(fd, offset, len, flags, &caller).await,
Op::Munmap {
fd,
offset,
len,
flags,
} => s.munmap(fd, offset, len, flags, &caller).await.map(|()| 0),
Op::Call {
fd,
payload,
metadata,
} => s.call(fd, payload, metadata).await,
Op::Getdents {
fd,
buf,
opaque_offset,
} => {
let buf = s.getdents(fd, buf, opaque_offset).await?;
Ok(buf.finalize())
}
},
))
Op::Dup { old_fd, buf } => {
let res = s.dup(old_fd, buf, &caller).await;
return Ok(Response::open_dup_like(self, res));
}
Op::Read {
fd,
buf,
offset,
flags,
} => s.read(fd, buf, offset, flags, &caller).await,
Op::Write {
fd,
buf,
offset,
flags,
} => s.write(fd, buf, offset, flags, &caller).await,
// TODO: Don't convert to usize
Op::Fsize { fd } => s.fsize(fd, &caller).await.map(|l| l as usize),
Op::Fchmod { fd, new_mode } => s.fchmod(fd, new_mode, &caller).await.map(|()| 0),
Op::Fchown {
fd,
new_uid,
new_gid,
} => s.fchown(fd, new_uid, new_gid, &caller).await.map(|()| 0),
Op::Fcntl { fd, cmd, arg } => s.fcntl(fd, cmd, arg, &caller).await,
Op::Fevent { fd, req_flags } => {
s.fevent(fd, req_flags, &caller).await.map(|f| f.bits())
}
Op::Fpath { fd, buf } => s.fpath(fd, buf, &caller).await,
Op::Frename { fd, new_path } => s.frename(fd, new_path, &caller).await,
Op::Fstat { fd, stat } => s.fstat(fd, stat, &caller).await.map(|()| 0),
Op::FstatVfs { fd, stat } => s.fstatvfs(fd, stat, &caller).await.map(|()| 0),
Op::Fsync { fd } => s.fsync(fd, &caller).await.map(|()| 0),
Op::Ftruncate { fd, new_sz } => s.ftruncate(fd, new_sz, &caller).await.map(|()| 0),
Op::Futimens { fd, times } => s.futimens(fd, times, &caller).await.map(|()| 0),
Op::MmapPrep {
fd,
offset,
len,
flags,
} => s.mmap_prep(fd, offset, len, flags, &caller).await,
Op::Munmap {
fd,
offset,
len,
flags,
} => s.munmap(fd, offset, len, flags, &caller).await.map(|()| 0),
Op::Call {
fd,
payload,
metadata,
} => s.call(fd, payload, metadata).await,
Op::Getdents {
fd,
buf,
opaque_offset,
} => {
let buf = s.getdents(fd, buf, opaque_offset).await?;
Ok(buf.finalize())
}
};
Ok(Response::new(self, res))
}
// TODO: Fix function coloring
pub fn handle_sync(self, s: &mut impl SchemeSync) -> Result<Response> {
let sqe = &self.inner.sqe;
let caller = CallerCtx {
pid: sqe.caller as usize,
uid: sqe.args[5] as u32,
gid: (sqe.args[5] >> 32) as u32,
};
let Some(op) = (unsafe { Op::from_sqe_unchecked(sqe) }) else {
return Ok(Response::new(&self, Err(Error::new(ENOSYS))));
let caller = self.caller();
let Some(op) = self.op() else {
return Ok(Response::new(self, Err(Error::new(ENOSYS))));
};
Ok(Response::new(
&self,
match op {
Op::Open { path, flags } => {
return Ok(Response::open_dup_like(&self, s.open(path, flags, &caller)))
}
Op::Rmdir { path } => s.rmdir(path, &caller).map(|()| 0),
Op::Unlink { path } => s.unlink(path, &caller).map(|()| 0),
let res = match op {
Op::Open { path, flags } => {
let res = s.open(path, flags, &caller);
return Ok(Response::open_dup_like(self, res));
}
Op::Rmdir { path } => s.rmdir(path, &caller).map(|()| 0),
Op::Unlink { path } => s.unlink(path, &caller).map(|()| 0),
Op::Dup { old_fd, buf } => {
return Ok(Response::open_dup_like(&self, s.dup(old_fd, buf, &caller)))
}
Op::Read {
fd,
buf,
offset,
flags,
} => s.read(fd, buf, offset, flags, &caller),
Op::Write {
fd,
buf,
offset,
flags,
} => s.write(fd, buf, offset, flags, &caller),
// TODO: Don't convert to usize
Op::Fsize { fd } => s.fsize(fd, &caller).map(|l| l as usize),
Op::Fchmod { fd, new_mode } => s.fchmod(fd, new_mode, &caller).map(|()| 0),
Op::Fchown {
fd,
new_uid,
new_gid,
} => s.fchown(fd, new_uid, new_gid, &caller).map(|()| 0),
Op::Fcntl { fd, cmd, arg } => s.fcntl(fd, cmd, arg, &caller),
Op::Fevent { fd, req_flags } => s.fevent(fd, req_flags, &caller).map(|f| f.bits()),
Op::Fpath { fd, buf } => s.fpath(fd, buf, &caller),
Op::Frename { fd, new_path } => s.frename(fd, new_path, &caller),
Op::Fstat { fd, stat } => s.fstat(fd, stat, &caller).map(|()| 0),
Op::FstatVfs { fd, stat } => s.fstatvfs(fd, stat, &caller).map(|()| 0),
Op::Fsync { fd } => s.fsync(fd, &caller).map(|()| 0),
Op::Ftruncate { fd, new_sz } => s.ftruncate(fd, new_sz, &caller).map(|()| 0),
Op::Futimens { fd, times } => s.futimens(fd, times, &caller).map(|()| 0),
Op::MmapPrep {
fd,
offset,
len,
flags,
} => s.mmap_prep(fd, offset, len, flags, &caller),
Op::Munmap {
fd,
offset,
len,
flags,
} => s.munmap(fd, offset, len, flags, &caller).map(|()| 0),
Op::Call {
fd,
payload,
metadata,
} => s.call(fd, payload, metadata),
Op::Getdents {
fd,
buf,
opaque_offset,
} => {
let buf = s.getdents(fd, buf, opaque_offset)?;
Ok(buf.finalize())
}
},
))
Op::Dup { old_fd, buf } => {
let res = s.dup(old_fd, buf, &caller);
return Ok(Response::open_dup_like(self, res));
}
Op::Read {
fd,
buf,
offset,
flags,
} => s.read(fd, buf, offset, flags, &caller),
Op::Write {
fd,
buf,
offset,
flags,
} => s.write(fd, buf, offset, flags, &caller),
// TODO: Don't convert to usize
Op::Fsize { fd } => s.fsize(fd, &caller).map(|l| l as usize),
Op::Fchmod { fd, new_mode } => s.fchmod(fd, new_mode, &caller).map(|()| 0),
Op::Fchown {
fd,
new_uid,
new_gid,
} => s.fchown(fd, new_uid, new_gid, &caller).map(|()| 0),
Op::Fcntl { fd, cmd, arg } => s.fcntl(fd, cmd, arg, &caller),
Op::Fevent { fd, req_flags } => s.fevent(fd, req_flags, &caller).map(|f| f.bits()),
Op::Fpath { fd, buf } => s.fpath(fd, buf, &caller),
Op::Frename { fd, new_path } => s.frename(fd, new_path, &caller),
Op::Fstat { fd, stat } => s.fstat(fd, stat, &caller).map(|()| 0),
Op::FstatVfs { fd, stat } => s.fstatvfs(fd, stat, &caller).map(|()| 0),
Op::Fsync { fd } => s.fsync(fd, &caller).map(|()| 0),
Op::Ftruncate { fd, new_sz } => s.ftruncate(fd, new_sz, &caller).map(|()| 0),
Op::Futimens { fd, times } => s.futimens(fd, times, &caller).map(|()| 0),
Op::MmapPrep {
fd,
offset,
len,
flags,
} => s.mmap_prep(fd, offset, len, flags, &caller),
Op::Munmap {
fd,
offset,
len,
flags,
} => s.munmap(fd, offset, len, flags, &caller).map(|()| 0),
Op::Call {
fd,
payload,
metadata,
} => s.call(fd, payload, metadata),
Op::Getdents {
fd,
buf,
opaque_offset,
} => {
let buf = s.getdents(fd, buf, opaque_offset)?;
Ok(buf.finalize())
}
};
Ok(Response::new(self, res))
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment