-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
allow building multicall binary as dynamic library #7928
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
GNU testsuite comparison:
|
Because the diff is deceptively large (git has no internal notion of moved files, so the "move + creating a new file at the old location" looks like two large edits instead), here's the actual main difference, from the output of diff --git a/src/bin/coreutils.rs b/src/lib/coreutils.rs
index b29e7ea23..68da02af1 100644
--- a/src/bin/coreutils.rs
+++ b/src/lib/coreutils.rs
@@ -20,7 +20,11 @@ const VERSION: &str = env!("CARGO_PKG_VERSION");
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
fn usage<T>(utils: &UtilityMap<T>, name: &str) {
- println!("{name} {VERSION} (multi-call binary)\n");
+ #[cfg(not(feature = "dynamic"))]
+ let kind = "binary";
+ #[cfg(feature = "dynamic")]
+ let kind = "dynamic library";
+ println!("{name} {VERSION} (multi-call {kind})\n");
println!("Usage: {name} [function [arguments...]]");
println!(" {name} --list\n");
println!("Options:");
@@ -51,7 +55,7 @@ fn name(binary_path: &Path) -> Option<&str> {
}
#[allow(clippy::cognitive_complexity)]
-fn main() {
+pub fn multicall_main() {
uucore::panic::mute_sigpipe_panic();
let utils = util_map();
@@ -239,3 +243,9 @@ fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command {
}
command
}
+
+#[cfg(feature = "dynamic")]
+#[unsafe(no_mangle)]
+pub extern "Rust" fn coreutils_multicall_main_wrapper() {
+ multicall_main();
+} The new |
isn't it just a workaround to bypass limitations of apparmor ? :) |
Right now, there's not a lot of advantage to dynamic linking. This is mainly a stepping stone to individual shim applications that would link against the one shared library. Once that exists, it's not so much a limitation of apparmor that's being addressed as the entire idea of restricting the capabilities of individual utilities. The multicall binary will never be able to do that, since there is no secure mechanism of figuring out how it was invoked ( |
If you are searching for inspiration, you can check how this is handled for gnu coreutils and busybox in ubuntu. It probably makes sense to handle it the same way for uutils coreutils. I know that in yocto meta-selinux, there are two busybox shell scripts which are one-liner files with different selinux labels: busybox, and busybox.nosuid. However their current implementation has the drawback of swallowing arg[0]. Also note that I fixed the build issue with musl in my PR "stdbuf: fix cross-compilation". |
The GNU coreutils in Ubuntu don't use the multi-call implementation, and it sounds like busybox is just given an extremely broad profile. The problem is that the individual uutils are much larger than the individual coreutils, and Ubuntu don't want to weaken their profiles when migrating. I suspect one reason the set of individual GNU utils is so much smaller is they can more heavily rely on direct invocation of the dynamically linked libc (e.g.,
This sounds similar to the other proposed solution by Ubuntu:
That works, as long as you're careful setting up the profiles, but swallows argv[0] as you say. Linking as a dynamic library also means we don't need exec to invoke any of the utilities, allowing tighter profiles. I would have thought that would also improve performance, but it looks like one or more of #include <unistd.h>
int main() {
char* path = "/home/user/Documents/coreutils/target/release/coreutils.static";
char* argv[2] = {"true", NULL};
char* env[1] = { NULL };
return execve(path, argv, env);
}
To summarize: individual GNU and uutils |
In the case of uutils-coreutils, I guess swallowing arg[0] is not an issue. I know in the case of busybox this breaks a corner-case with "sh" where you can change arg[0] and pass a dash as arg[0] to make it a login shell, but I don't think there is such a corner-case in uutils-coreutils. |
I was looking at the options and i believe that the dynamic linking approach is the best one to get AppArmor profiles (or selinux ones I suppose) working correctly. The problem with the multicall binary even with the wrapper binaries is that you still need to allow the multi-call binary to be run; i.e. you allow For the performance I think a bunch of that is down to relocations of the larger code base? What's then missing is generating tiny individual binaries that swallow argv[0] and insert the hardcoded one. Essentially my thought was the library exposes the C-style |
It was noted in the latest Ubuntu post that they were having problems with AppArmor profiles on the multicall binary, and listed a possible solution of
I decided to see how much work that would be, and it turns out to not be too bad. Basically, I just moved the multicall binary logic into a top-level library, and made the multicall binary link to it, either statically (i.e., normal Rust linking) or dynamically, depending on whether the "dynamic" feature is enabled. On my Debian system with
--release
, the normalcoreutils
(i.e., the default multicall binary) is ~14M, while when dynamically linking,libcoreutils.so
is ~26M andcoreutils
(i.e., the minimal shim that loads and executes the shared library) is 381K.This allows creating individual binaries with different profiles by just copying the dynamically linked multicall binary with the name of each respective utility. It's only marginally better for security though, since the behavior of the shim executable is identical to the full multicall binary, meaning someone could just modify
argv[0]
to get the coreutilfoo
to run with the AppArmor profile of coreutilbar
, or exploit a confused deputy with setuid, etc. There are a few possible solutions to that (for a future PR), with the main gist being to get each util's (uu)main dynamically linked when thedynamic
feature is enabled. Another thing I'm putting off is proper Windows support in the documentation and CICD. I don't know as much about Windows/.dll
s as I do Unix shared libraries, and don't have a local Windows dev env right now, so I'm not testing it at all yet, but in theory it should work.Some things I'd welcome any feedback on:
LD_LIBRARY_PATH
works fine for local configurations..so
generation with musl yet), to ensure it loads fine, but not run the tests with it. On the other hand, the default test suite is rarely the bottleneck in CICD, so we could run it if we think that overhead is acceptable.