Skip to content

Decide policy on stub implementations (syscalls, sockopts, flags, etc) #3280

@stevenengler

Description

@stevenengler

Each time we need to decide whether to add a stub implementation we've mentioned that we should have a policy somewhere for what to do. This issue can be where we decide what that policy should be.

There are many syscalls, socket options, syscall flags, etc that Shadow doesn't support but applications use. Typically we want to return an error (ex: EOPNOTSUPP or EINVAL) for these unsupported features. If we're lucky the application will just ignore the error, but many applications will then exit with an error message. If we want the application to run in Shadow the choices are to patch the application, add support in Shadow, or add a stub in Shadow which returns "success" without actually performing the correctly. If the feature is complex and/or no one has time to work on it, the simplest solution is often to add a stub in Shadow. But this means we're intentionally adding incorrect behaviour to Shadow, which can lead to confusion for users and can make Shadow simulations much harder to debug. In the worst case it might cause Shadow to generate incorrect simulation results.

We already have many stub implementations. For example:

(libc::SOL_SOCKET, libc::SO_REUSEADDR) => {
// TODO: implement this, tor and tgen use it
log::trace!("setsockopt SO_REUSEADDR not yet implemented");
}
(libc::SOL_SOCKET, libc::SO_REUSEPORT) => {
// TODO: implement this, tgen uses it
log::trace!("setsockopt SO_REUSEPORT not yet implemented");
}
(libc::SOL_SOCKET, libc::SO_KEEPALIVE) => {
// TODO: implement this, libevent uses it in
// evconnlistener_new_bind()
log::trace!("setsockopt SO_KEEPALIVE not yet implemented");
}
(libc::SOL_SOCKET, libc::SO_BROADCAST) => {
// TODO: implement this, pkg.go.dev/net uses it
log::trace!("setsockopt SO_BROADCAST not yet implemented");
}

if options.contains(WaitFlags::__WNOTHREAD) {
// TODO: track parent thread and check it here.
warn_once_then_debug!("__WNOTHREAD unimplemented; ignoring.");
}

/// Resource usage, as returned e.g. by the `getrusage` syscall.
pub fn rusage(&self) -> linux_api::resource::rusage {
warn_once_then_debug!(
"resource usage (rusage) tracking unimplemented; Returning bogus zeroed values"
);
// TODO: Actually track some of these.

(and more)

The question is: What do we do when adding a stub implementation is the simplest solution to unblocking an application or use-case?

Some possible answers:

  • Don't allow any new stubs, but keep existing stubs.
  • Allow stubs for tor-related applications only.
  • Allow all stubs as long as they also have a warning (typically warn_once_then_debug!()).
  • Add an experimental config "stub" option which allows users to opt-into a list of stubs. This list would default to the stubs we already have and use, but new stubs would not be enabled by default.

We might want some "escape hatches" from the above rules as well, such as:

  • Allow new default stubs for features that are required for all applications. (For example the rseq syscall, which is partly implemented and is needed for modern glibc versions.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions