Initial support for securing tty I/O. (#684)
* Initial support for securing tty I/O. * Update the tests. * Fix warnings * Update crates/wasi-common/src/fdentry.rs Co-Authored-By: Jakub Konka <jakub.konka@golem.network> * Properly sandbox stderr. * Document why the scratch buffer is 4 elements long. * Update crates/wasi-common/src/sandboxed_tty_writer.rs Co-Authored-By: Jakub Konka <jakub.konka@golem.network> * Update crates/wasi-common/src/sandboxed_tty_writer.rs Co-Authored-By: Jakub Konka <jakub.konka@golem.network> * Add comments explaining how we report the number of bytes written. * Always sanitize stderr. * Port the changes to the snapshot_0 directory. * Fix snapshot_0 compilation error. * Replace the scratch buffer with a temporary buffer. * Update crates/wasi-common/src/sandboxed_tty_writer.rs Co-Authored-By: bjorn3 <bjorn3@users.noreply.github.com> * Format with latest stable rustfmt. Co-authored-by: Jakub Konka <kubkon@jakubkonka.com> Co-authored-by: bjorn3 <bjorn3@users.noreply.github.com>
This commit is contained in:
@@ -162,6 +162,14 @@ impl FdEntry {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether this descriptor is considered a tty within WASI.
|
||||
/// Note that since WASI itself lacks an `isatty` syscall and relies
|
||||
/// on a conservative approximation, we use the same approximation here.
|
||||
pub(crate) fn isatty(&self) -> bool {
|
||||
self.file_type == wasi::__WASI_FILETYPE_CHARACTER_DEVICE
|
||||
&& (self.rights_base & (wasi::__WASI_RIGHTS_FD_SEEK | wasi::__WASI_RIGHTS_FD_TELL)) == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// This allows an `OsHandle` to be temporarily borrowed from a
|
||||
|
||||
@@ -8,10 +8,12 @@ use crate::old::snapshot_0::sys::fdentry_impl::determine_type_rights;
|
||||
use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::path_open_rights;
|
||||
use crate::old::snapshot_0::sys::{host_impl, hostcalls_impl};
|
||||
use crate::old::snapshot_0::{helpers, host, wasi, wasi32, Error, Result};
|
||||
use crate::sandboxed_tty_writer::SandboxedTTYWriter;
|
||||
use filetime::{set_file_handle_times, FileTime};
|
||||
use log::trace;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
use std::ops::DerefMut;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> {
|
||||
@@ -160,7 +162,7 @@ pub(crate) unsafe fn fd_read(
|
||||
.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_READ, 0)?
|
||||
{
|
||||
Descriptor::OsHandle(file) => file.read_vectored(&mut iovs),
|
||||
Descriptor::Stdin => io::stdin().lock().read_vectored(&mut iovs),
|
||||
Descriptor::Stdin => io::stdin().read_vectored(&mut iovs),
|
||||
_ => return Err(Error::EBADF),
|
||||
};
|
||||
|
||||
@@ -357,21 +359,35 @@ pub(crate) unsafe fn fd_write(
|
||||
let iovs: Vec<io::IoSlice> = iovs.iter().map(|vec| host::ciovec_to_host(vec)).collect();
|
||||
|
||||
// perform unbuffered writes
|
||||
let host_nwritten = match wasi_ctx
|
||||
.get_fd_entry_mut(fd)?
|
||||
.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_WRITE, 0)?
|
||||
{
|
||||
Descriptor::OsHandle(file) => file.write_vectored(&iovs)?,
|
||||
let entry = wasi_ctx.get_fd_entry_mut(fd)?;
|
||||
let isatty = entry.isatty();
|
||||
let desc = entry.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_WRITE, 0)?;
|
||||
let host_nwritten = match desc {
|
||||
Descriptor::OsHandle(file) => {
|
||||
if isatty {
|
||||
SandboxedTTYWriter::new(file.deref_mut()).write_vectored(&iovs)?
|
||||
} else {
|
||||
file.write_vectored(&iovs)?
|
||||
}
|
||||
}
|
||||
Descriptor::Stdin => return Err(Error::EBADF),
|
||||
Descriptor::Stdout => {
|
||||
// lock for the duration of the scope
|
||||
let stdout = io::stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
let nwritten = stdout.write_vectored(&iovs)?;
|
||||
let nwritten = if isatty {
|
||||
SandboxedTTYWriter::new(&mut stdout).write_vectored(&iovs)?
|
||||
} else {
|
||||
stdout.write_vectored(&iovs)?
|
||||
};
|
||||
stdout.flush()?;
|
||||
nwritten
|
||||
}
|
||||
Descriptor::Stderr => io::stderr().lock().write_vectored(&iovs)?,
|
||||
// Always sanitize stderr, even if it's not directly connected to a tty,
|
||||
// because stderr is meant for diagnostics rather than binary output,
|
||||
// and may be redirected to a file which could end up being displayed
|
||||
// on a tty later.
|
||||
Descriptor::Stderr => SandboxedTTYWriter::new(&mut io::stderr()).write_vectored(&iovs)?,
|
||||
};
|
||||
|
||||
trace!(" | *nwritten={:?}", host_nwritten);
|
||||
|
||||
Reference in New Issue
Block a user