Eyra does the impossible
Posted on
Eyra is challenging ideas about what it means to be a libc. In doing so, it's doing a few things often considered to be... impossible 😉.
Fixing Rust's set_var
unsoundness
rust-lang/rust#27970 is a soundness bug in Rust. It is a way that Rust programs
can segfault without using any unsafe
code. The bug was opened 8 years ago,
and it's widely believed to be impossible to fully fix.
One of the reasons it's so difficult is that it isn't enough to do locking,
even inside libc, because getenv
returns pointers to memory that need to stay
valid after the getenv
call returns. And, getenv
/setenv
/etc. can be called
by arbitrary C code, so any scheme that relies on all callers of getenv
following a particular protocol isn't reliable.
Eyra solves this by having setenv
etc. just leak the old memory. That ensures
that it stays valid for as long as any thread needs it. Granted, leaking isn't
great, and Eyra makes it configurable with the "threadsafe-setenv" cargo feature,
so it can be disabled in favor of the thread-unsafe implementation. However that
said, leaking is not unsafe, and for some use cases, it'll be less bad than
the possibility of undefined behavior from dangling pointers.
Eyra currently only supports Linux, and has other limitations, so it isn't a full solution for all of Rust, but it does solve the problem within its range.
Full host NSS and DNS support without dynamic linking
In glibc, a statically-linked binary, despite being statically linked,
heroically includes the ability to dlopen
NSS libraries as needed in order
to implement the lookup rules defined in "/etc/nsswitch.conf", and this means
that statically-linked binaries end up depending on the versions of the NSS
libraries that match the glibc version they were built with.
In musl, a statically-linked binary just hard-codes the basic NSS and DNS resolution strategies. It knows how to read "/etc/passwd", "/etc/resolv.conf", and other files, and do the main things that one does with those files, so it doesn't respect "/etc/nsswitch.conf" at all, and doesn't use the same name lookup logic as other programs on a glibc-based distribution.
These have long been the only options, but Eyra does something different.
In Eyra, NSS functions are implemented by executing the external getent
program, and parsing its output. getent
is present on both glibc
-based
and musl
-based Linux distributions, and has a stable command-line interface,
so Eyra programs do not depend on a specific version of libc being installed,
and it follows the system NSS and DNS configuration.
(And to be sure, using getent
like this won't work for all use cases, so
Eyra may add other options in the future.)
Compiling whole programs with a single cargo build
.
Eyra is not the only system capable of whole-program optimization, however to
my knowledge, it is the only system that can compile a whole program, entirely
from source, in a single cargo build
invocation.
This is achieved by using cargo's -Z build-std
option, which builds libcore,
liballoc, libstd, and other Rust libraries from source, by using Eyra which
builds the libc implementation from source, and, for completeness, by disabling
Eyra's "use-compiler-builtins"
feature, which tells it to use its own
implementations of memcpy
etc. instead of linking to Rust's prebuilt
compiler_builtins
library. Thay way, everything down to the OS boundary is
just a cargo dependency built from source. See the all-from-source example
for more details.
This may be useful for doing whole-program static analysis, for using non-standard calling conventions, or anything else that requires that the whole program be compiled together.
What's Eyra?
Eyra is currently a side project that I'm building for fun. If you think it sounds interesting, please reach out!