Browse Source

Allow to disable clocks in WasiCtx (#6007)

Takes the approach described in #6004, but also creates a wrapper for the monotonic time that encapsulates the `creation_time` field as well, since they logically belong and are always used together.

This makes it easier to configure `WasiCtx` with custom clocks as well as disable them for security or determinism reasons.

Closes #6004.
pull/6016/head
Ingvar Stepanyan 2 years ago
committed by GitHub
parent
commit
873d3b50a0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      crates/wasi-common/cap-std-sync/src/clocks.rs
  2. 51
      crates/wasi-common/src/clocks.rs
  3. 19
      crates/wasi-common/src/snapshots/preview_0.rs
  4. 35
      crates/wasi-common/src/snapshots/preview_1.rs
  5. 31
      crates/wasi-common/tokio/tests/poll_oneoff.rs

12
crates/wasi-common/cap-std-sync/src/clocks.rs

@ -35,13 +35,7 @@ impl WasiMonotonicClock for MonotonicClock {
}
pub fn clocks_ctx() -> WasiClocks {
let system = Box::new(SystemClock::new(ambient_authority()));
let monotonic = cap_std::time::MonotonicClock::new(ambient_authority());
let creation_time = monotonic.now();
let monotonic = Box::new(MonotonicClock(monotonic));
WasiClocks {
system,
monotonic,
creation_time,
}
WasiClocks::new()
.with_system(SystemClock::new(ambient_authority()))
.with_monotonic(MonotonicClock::new(ambient_authority()))
}

51
crates/wasi-common/src/clocks.rs

@ -1,3 +1,4 @@
use crate::{Error, ErrorExt};
use cap_std::time::{Duration, Instant, SystemTime};
pub enum SystemTimeSpec {
@ -15,8 +16,52 @@ pub trait WasiMonotonicClock: Send + Sync {
fn now(&self, precision: Duration) -> Instant;
}
pub struct WasiClocks {
pub system: Box<dyn WasiSystemClock>,
pub monotonic: Box<dyn WasiMonotonicClock>,
pub struct WasiMonotonicOffsetClock {
pub creation_time: cap_std::time::Instant,
pub abs_clock: Box<dyn WasiMonotonicClock>,
}
impl WasiMonotonicOffsetClock {
pub fn new(clock: impl 'static + WasiMonotonicClock) -> Self {
Self {
creation_time: clock.now(clock.resolution()),
abs_clock: Box::new(clock),
}
}
}
pub struct WasiClocks {
pub system: Option<Box<dyn WasiSystemClock>>,
pub monotonic: Option<WasiMonotonicOffsetClock>,
}
impl WasiClocks {
pub fn new() -> Self {
Self {
system: None,
monotonic: None,
}
}
pub fn with_system(mut self, clock: impl 'static + WasiSystemClock) -> Self {
self.system = Some(Box::new(clock));
self
}
pub fn with_monotonic(mut self, clock: impl 'static + WasiMonotonicClock) -> Self {
self.monotonic = Some(WasiMonotonicOffsetClock::new(clock));
self
}
pub fn system(&self) -> Result<&dyn WasiSystemClock, Error> {
self.system
.as_deref()
.ok_or_else(|| Error::badf().context("system clock is not supported"))
}
pub fn monotonic(&self) -> Result<&WasiMonotonicOffsetClock, Error> {
self.monotonic
.as_ref()
.ok_or_else(|| Error::badf().context("monotonic clock is not supported"))
}
}

19
crates/wasi-common/src/snapshots/preview_0.rs

@ -959,25 +959,22 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
match sub.u {
types::SubscriptionU::Clock(clocksub) => match clocksub.id {
types::Clockid::Monotonic => {
let clock = self.clocks.monotonic.deref();
let clock = self.clocks.monotonic()?;
let precision = Duration::from_nanos(clocksub.precision);
let duration = Duration::from_nanos(clocksub.timeout);
let deadline = if clocksub
let start = if clocksub
.flags
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
{
self.clocks
.creation_time
.checked_add(duration)
.ok_or_else(|| Error::overflow().context("deadline"))?
clock.creation_time
} else {
clock
.now(precision)
.checked_add(duration)
.ok_or_else(|| Error::overflow().context("deadline"))?
clock.abs_clock.now(precision)
};
let deadline = start
.checked_add(duration)
.ok_or_else(|| Error::overflow().context("deadline"))?;
poll.subscribe_monotonic_clock(
clock,
&*clock.abs_clock,
deadline,
precision,
sub.userdata.into(),

35
crates/wasi-common/src/snapshots/preview_1.rs

@ -68,8 +68,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
async fn clock_res_get(&mut self, id: types::Clockid) -> Result<types::Timestamp, Error> {
let resolution = match id {
types::Clockid::Realtime => Ok(self.clocks.system.resolution()),
types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()),
types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()),
types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()),
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
Err(Error::badf().context("process and thread clocks are not supported"))
}
@ -85,7 +85,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
let precision = Duration::from_nanos(precision);
match id {
types::Clockid::Realtime => {
let now = self.clocks.system.now(precision).into_std();
let now = self.clocks.system()?.now(precision).into_std();
let d = now
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map_err(|_| {
@ -94,8 +94,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(d.as_nanos().try_into()?)
}
types::Clockid::Monotonic => {
let now = self.clocks.monotonic.now(precision);
let d = now.duration_since(self.clocks.creation_time);
let clock = self.clocks.monotonic()?;
let now = clock.abs_clock.now(precision);
let d = now.duration_since(clock.creation_time);
Ok(d.as_nanos().try_into()?)
}
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
@ -909,25 +910,22 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
match sub.u {
types::SubscriptionU::Clock(clocksub) => match clocksub.id {
types::Clockid::Monotonic => {
let clock = self.clocks.monotonic.deref();
let clock = self.clocks.monotonic()?;
let precision = Duration::from_nanos(clocksub.precision);
let duration = Duration::from_nanos(clocksub.timeout);
let deadline = if clocksub
let start = if clocksub
.flags
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
{
self.clocks
.creation_time
.checked_add(duration)
.ok_or_else(|| Error::overflow().context("deadline"))?
clock.creation_time
} else {
clock
.now(precision)
.checked_add(duration)
.ok_or_else(|| Error::overflow().context("deadline"))?
clock.abs_clock.now(precision)
};
let deadline = start
.checked_add(duration)
.ok_or_else(|| Error::overflow().context("deadline"))?;
poll.subscribe_monotonic_clock(
clock,
&*clock.abs_clock,
deadline,
precision,
sub.userdata.into(),
@ -939,7 +937,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
// on threads waiting in these functions. MONOTONIC should always have
// resolution at least as good as REALTIME, so we can translate a
// non-absolute `REALTIME` request into a `MONOTONIC` request.
let clock = self.clocks.monotonic.deref();
let clock = self.clocks.monotonic()?;
let precision = Duration::from_nanos(clocksub.precision);
let duration = Duration::from_nanos(clocksub.timeout);
let deadline = if clocksub
@ -949,12 +947,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
return Err(Error::not_supported());
} else {
clock
.abs_clock
.now(precision)
.checked_add(duration)
.ok_or_else(|| Error::overflow().context("deadline"))?
};
poll.subscribe_monotonic_clock(
clock,
&*clock.abs_clock,
deadline,
precision,
sub.userdata.into(),

31
crates/wasi-common/tokio/tests/poll_oneoff.rs

@ -38,14 +38,14 @@ async fn empty_file_readable() -> Result<(), Error> {
let mut poll = Poll::new();
poll.subscribe_read(&mut *f, Userdata::from(123));
// Timeout bounds time in poll_oneoff
let monotonic = &*clocks.monotonic()?.abs_clock;
poll.subscribe_monotonic_clock(
&*clocks.monotonic,
clocks
.monotonic
.now(clocks.monotonic.resolution())
monotonic,
monotonic
.now(monotonic.resolution())
.checked_add(TIMEOUT)
.unwrap(),
clocks.monotonic.resolution(),
monotonic.resolution(),
Userdata::from(0),
);
poll_oneoff(&mut poll).await?;
@ -81,14 +81,14 @@ async fn empty_file_writable() -> Result<(), Error> {
let mut poll = Poll::new();
poll.subscribe_write(&mut *writable_f, Userdata::from(123));
// Timeout bounds time in poll_oneoff
let monotonic = &*clocks.monotonic()?.abs_clock;
poll.subscribe_monotonic_clock(
&*clocks.monotonic,
clocks
.monotonic
.now(clocks.monotonic.resolution())
monotonic,
monotonic
.now(monotonic.resolution())
.checked_add(TIMEOUT)
.unwrap(),
clocks.monotonic.resolution(),
monotonic.resolution(),
Userdata::from(0),
);
poll_oneoff(&mut poll).await?;
@ -110,9 +110,9 @@ async fn empty_file_writable() -> Result<(), Error> {
async fn stdio_readable() -> Result<(), Error> {
let clocks = clocks_ctx();
let deadline = clocks
.monotonic
.now(clocks.monotonic.resolution())
let monotonic = &*clocks.monotonic()?.abs_clock;
let deadline = monotonic
.now(monotonic.resolution())
.checked_add(TIMEOUT)
.unwrap();
@ -133,10 +133,11 @@ async fn stdio_readable() -> Result<(), Error> {
poll.subscribe_write(&mut **file, Userdata::from(*ix));
}
// Timeout bounds time in poll_oneoff
let monotonic = &*clocks.monotonic()?.abs_clock;
poll.subscribe_monotonic_clock(
&*clocks.monotonic,
monotonic,
deadline,
clocks.monotonic.resolution(),
monotonic.resolution(),
Userdata::from(999),
);
poll_oneoff(&mut poll).await?;

Loading…
Cancel
Save