Skip to content

read_buf and RtVdsoVtable provenance #27

@thaliaarchi

Description

@thaliaarchi

I found Motor OS after the std implementation merged today (congrats on that!) and took a look around.

I noticed that you use the default Read::read_buf implementation, since your runtime read takes &mut [u8]. I've added read_buf implementations much more widely throughout std as a pet project (I want to see it stabilized). For the Hermit unikernel, since it's linked to a user program as a library, I changed their read to take &mut [MaybeUninit<u8>] so it works for both and is sound for their C FFI (hermit-os/kernel#1606). Conversely, for RISC Zero, although they take initialized memory for read, any memory from the guest was already initialized by the host, so it is safe to implement read_buf with it (risc0/risc0#2853). In your case, I don't know which it falls under. It has stronger separation from user programs than Hermit, but I doubt the unique guarantee of RISC Zero holds here.

fn read(file: &File, buf: &mut [u8]) -> Result<usize, ErrorCode> {

Additionally, RtVdsoVtable extensively violates strict provenance by casting function pointers to u64. Is there some reason you're using AtomicU64 instead of AtomicPtr<_>?

pub struct RtVdsoVtable {
// This function is called to initialize this VTable
// (i.e. all fields other than vdso_entry and vdso_bytes_sz).
// Initialized by the loader/parent.
pub vdso_entry: AtomicU64,
// The size of vdso bytes (the address is fixed at RT_VDSO_BYTES_ADDR).
// Initialized by the loader/parent.
pub vdso_bytes_sz: AtomicU64,
// Memory management.
pub alloc: AtomicU64,
pub alloc_zeroed: AtomicU64,
pub realloc: AtomicU64,
pub dealloc: AtomicU64,
pub release_handle: AtomicU64,
// Time management.
pub time_instant_now: AtomicU64,
pub time_ticks_to_nanos: AtomicU64,
pub time_nanos_to_ticks: AtomicU64,
pub time_ticks_in_sec: AtomicU64,
pub time_abs_ticks_to_nanos: AtomicU64,
// Futex.
pub futex_wait: AtomicU64,
pub futex_wake: AtomicU64,
pub futex_wake_all: AtomicU64,
// Process-related.
pub proc_args: AtomicU64,
pub proc_get_full_env: AtomicU64,
pub proc_getenv: AtomicU64,
pub proc_setenv: AtomicU64,
pub proc_spawn: AtomicU64,
pub proc_kill: AtomicU64,
pub proc_wait: AtomicU64,
pub proc_status: AtomicU64,
pub proc_exit: AtomicU64,
// Thread Local Storage.
pub tls_create: AtomicU64,
pub tls_set: AtomicU64,
pub tls_get: AtomicU64,
pub tls_destroy: AtomicU64,
// Thread management.
pub thread_spawn: AtomicU64,
pub thread_sleep: AtomicU64,
pub thread_yield: AtomicU64,
pub thread_set_name: AtomicU64,
pub thread_join: AtomicU64,
// Filesystem.
pub fs_is_terminal: AtomicU64,
pub fs_open: AtomicU64,
pub fs_close: AtomicU64,
pub fs_get_file_attr: AtomicU64,
pub fs_fsync: AtomicU64,
pub fs_datasync: AtomicU64,
pub fs_truncate: AtomicU64,
pub fs_read: AtomicU64,
pub fs_read_vectored: AtomicU64,
pub fs_write: AtomicU64,
pub fs_write_vectored: AtomicU64,
pub fs_flush: AtomicU64,
pub fs_seek: AtomicU64,
pub fs_mkdir: AtomicU64,
pub fs_unlink: AtomicU64,
pub fs_rename: AtomicU64,
pub fs_rmdir: AtomicU64,
pub fs_rmdir_all: AtomicU64,
pub fs_set_perm: AtomicU64,
pub fs_set_file_perm: AtomicU64,
pub fs_stat: AtomicU64,
pub fs_canonicalize: AtomicU64,
pub fs_copy: AtomicU64,
pub fs_opendir: AtomicU64,
pub fs_closedir: AtomicU64,
pub fs_readdir: AtomicU64,
pub fs_getcwd: AtomicU64,
pub fs_chdir: AtomicU64,
pub fs_duplicate: AtomicU64,
// Networking.
pub dns_lookup: AtomicU64,
pub net_bind: AtomicU64,
pub net_listen: AtomicU64,
pub net_accept: AtomicU64,
pub net_tcp_connect: AtomicU64,
pub net_udp_connect: AtomicU64,
pub net_socket_addr: AtomicU64,
pub net_peer_addr: AtomicU64,
pub net_setsockopt: AtomicU64,
pub net_getsockopt: AtomicU64,
pub net_peek: AtomicU64,
pub net_udp_recv_from: AtomicU64,
pub net_udp_peek_from: AtomicU64,
pub net_udp_send_to: AtomicU64,
pub net_udp_multicast_op_v4: AtomicU64,
pub net_udp_multicast_op_v6: AtomicU64,
// Polling.
pub poll_new: AtomicU64,
pub poll_add: AtomicU64,
pub poll_set: AtomicU64,
pub poll_del: AtomicU64,
pub poll_wait: AtomicU64,
pub poll_wake: AtomicU64,
// Some utilities.
pub log_to_kernel: AtomicU64,
pub log_backtrace: AtomicU64,
pub fill_random_bytes: AtomicU64,
pub num_cpus: AtomicU64,
pub internal_helper: AtomicU64,
pub current_exe: AtomicU64,
}

pub extern "C" fn motor_start(version: u64) {
if version != RT_VERSION {
// Doing an assert or panic will #PF, so we use lower-level API.
moto_log!("VDSO: unsupported version: {version}.");
moto_sys::sys_cpu::SysCpu::exit(1)
}
let vtable = RtVdsoVtable::get();
let self_addr = motor_start as *const () as usize as u64;
assert_eq!(vtable.vdso_entry.load(Ordering::Acquire), self_addr);
// Memory management.
vtable.alloc.store(
rt_alloc::alloc as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.alloc_zeroed.store(
rt_alloc::alloc_zeroed as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.dealloc.store(
rt_alloc::dealloc as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.realloc.store(
rt_alloc::realloc as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.release_handle.store(
rt_alloc::release_handle as *const () as usize as u64,
Ordering::Relaxed,
);
// Time management.
vtable.time_instant_now.store(
rt_time::time_instant_now as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.time_ticks_to_nanos.store(
rt_time::ticks_to_nanos as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.time_nanos_to_ticks.store(
rt_time::nanos_to_ticks as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.time_ticks_in_sec.store(
moto_sys::KernelStaticPage::get().tsc_in_sec,
Ordering::Relaxed,
);
vtable.time_abs_ticks_to_nanos.store(
rt_time::abs_ticks_to_nanos as *const () as usize as u64,
Ordering::Relaxed,
);
// Futex.
vtable.futex_wait.store(
rt_futex::futex_wait as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.futex_wake.store(
rt_futex::futex_wake as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.futex_wake_all.store(
rt_futex::futex_wake_all as *const () as usize as u64,
Ordering::Relaxed,
);
// Process-related.
vtable.proc_args.store(
rt_process::args as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_get_full_env.store(
rt_process::get_full_env as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_getenv.store(
rt_process::getenv as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_setenv.store(
rt_process::setenv as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_spawn.store(
rt_process::spawn as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_kill.store(
rt_process::kill as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_wait.store(
rt_process::wait as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_status.store(
rt_process::status as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.proc_exit.store(
rt_process::exit as *const () as usize as u64,
Ordering::Relaxed,
);
// Thread Local Storage.
vtable.tls_create.store(
rt_tls::create as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.tls_set
.store(rt_tls::set as *const () as usize as u64, Ordering::Relaxed);
vtable
.tls_get
.store(rt_tls::get as *const () as usize as u64, Ordering::Relaxed);
vtable.tls_destroy.store(
rt_tls::destroy as *const () as usize as u64,
Ordering::Relaxed,
);
// Thread management.
vtable.thread_spawn.store(
rt_thread::spawn as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.thread_yield.store(
rt_thread::yield_now as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.thread_sleep.store(
rt_thread::sleep as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.thread_set_name.store(
rt_thread::set_name as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.thread_join.store(
rt_thread::join as *const () as usize as u64,
Ordering::Relaxed,
);
// Filesystem.
vtable.fs_is_terminal.store(
rt_fs::is_terminal as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.fs_open
.store(rt_fs::open as *const () as usize as u64, Ordering::Relaxed);
vtable.fs_close.store(
posix::posix_close as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_get_file_attr.store(
rt_fs::get_file_attr as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.fs_fsync
.store(rt_fs::fsync as *const () as usize as u64, Ordering::Relaxed);
vtable.fs_datasync.store(
rt_fs::datasync as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_truncate.store(
rt_fs::truncate as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_read.store(
posix::posix_read as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_read_vectored.store(
posix::posix_read_vectored as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_write.store(
posix::posix_write as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_write_vectored.store(
posix::posix_write_vectored as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_flush.store(
posix::posix_flush as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.fs_seek
.store(rt_fs::seek as *const () as usize as u64, Ordering::Relaxed);
vtable
.fs_mkdir
.store(rt_fs::mkdir as *const () as usize as u64, Ordering::Relaxed);
vtable.fs_unlink.store(
rt_fs::unlink as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_rename.store(
rt_fs::rename as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.fs_rmdir
.store(rt_fs::rmdir as *const () as usize as u64, Ordering::Relaxed);
vtable.fs_rmdir_all.store(
rt_fs::rmdir_all as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_set_perm.store(
rt_fs::set_perm as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_set_file_perm.store(
rt_fs::set_file_perm as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.fs_stat
.store(rt_fs::stat as *const () as usize as u64, Ordering::Relaxed);
vtable.fs_canonicalize.store(
rt_fs::canonicalize as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.fs_copy
.store(rt_fs::copy as *const () as usize as u64, Ordering::Relaxed);
vtable.fs_opendir.store(
rt_fs::opendir as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_closedir.store(
rt_fs::closedir as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_readdir.store(
rt_fs::readdir as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fs_getcwd.store(
rt_fs::getcwd as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.fs_chdir
.store(rt_fs::chdir as *const () as usize as u64, Ordering::Relaxed);
vtable.fs_duplicate.store(
posix::posix_duplicate as *const () as usize as u64,
Ordering::Relaxed,
);
// Networking.
vtable.dns_lookup.store(
net::rt_net::dns_lookup as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_bind.store(
net::rt_net::bind as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_listen.store(
net::rt_net::listen as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_accept.store(
net::rt_net::accept as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_tcp_connect.store(
net::rt_net::tcp_connect as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_udp_connect.store(
net::rt_net::udp_connect as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_socket_addr.store(
net::rt_net::socket_addr as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_peer_addr.store(
net::rt_net::peer_addr as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_setsockopt.store(
net::rt_net::setsockopt as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_getsockopt.store(
net::rt_net::getsockopt as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_peek.store(
net::rt_net::peek as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_udp_recv_from.store(
net::rt_net::udp_recv_from as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_udp_peek_from.store(
net::rt_net::udp_peek_from as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_udp_send_to.store(
net::rt_net::udp_send_to as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_udp_multicast_op_v4.store(
vdso_unimplemented as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.net_udp_multicast_op_v6.store(
vdso_unimplemented as *const () as usize as u64,
Ordering::Relaxed,
);
// Poll.
vtable
.poll_new
.store(rt_poll::new as *const () as usize as u64, Ordering::Relaxed);
vtable
.poll_add
.store(rt_poll::add as *const () as usize as u64, Ordering::Relaxed);
vtable
.poll_set
.store(rt_poll::set as *const () as usize as u64, Ordering::Relaxed);
vtable
.poll_del
.store(rt_poll::del as *const () as usize as u64, Ordering::Relaxed);
vtable.poll_wait.store(
rt_poll::wait as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.poll_wake.store(
rt_poll::wake as *const () as usize as u64,
Ordering::Relaxed,
);
// Misc.
vtable.log_to_kernel.store(
util::logging::log_to_kernel as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.log_backtrace.store(
util::logging::log_backtrace as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.fill_random_bytes.store(
fill_random_bytes as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.internal_helper.store(
vdso_internal_helper as *const () as usize as u64,
Ordering::Relaxed,
);
vtable.current_exe.store(
rt_process::current_exe as *const () as usize as u64,
Ordering::Relaxed,
);
vtable
.num_cpus
.store(num_cpus as *const () as usize as u64, Ordering::Relaxed);
// The final fence.
core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
let _ = moto_sys::set_current_thread_name("main");
stdio::init();
}

Although today's nightly includes the Motor PR, I am unable to install the target. The current instructions in docs/build.md also do not work for me. I'll try again tomorrow?

$ rustup override set nightly-2025-10-17
$ rustup target remove x86_64-unknown-motor --toolchain nightly-2025-10-17
warn: removing the last target; no build targets will be available
error: toolchain 'nightly-2025-10-17-x86_64-unknown-linux-gnu' does not have target 'x86_64-unknown-motor' installed

If either of these issues are worth fixing, I'll send PRs as soon as I can get a local build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions