-
-
Notifications
You must be signed in to change notification settings - Fork 563
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
Statically linked runtime #877
Comments
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Actually we are very interested in supporting this, as it would allow us to close #1015 - correct? Let's collaborate 👍 |
Shall we state "get rid of FUSE" as a goal? |
Do you think we can change it just so much that it can at least work on Alpine when libc6-compat is installed there? Then it would not even have to be fully static. See #1015 |
A completely static linked app would also allow to create docker images without userland. E.g. Only the static linked app without any linux userland surrounding it... That's not only smaller, but also decreases the attack surface. |
@TheAssassin would that be something that you think would be doable if we would rewrite the runtime in, say, Rust? Wouldn't the runtime be rather large then because it would have to statically link libfuse? (How large would it become?) Or should we try to get rid of FUSE altogether for the future type 3 AppImages? |
You don't need the entire libfuse, you just need a few bits. I've read a bit into fuse-rs, it doesn't seem that complex to me. The size is secondary; we can save bloat elsewhere (e.g., by using musl libc properly thinned down to the essential bits, etc.). Getting rid of FUSE would be awesome, but I have doubts it's all that easy. |
No. No hard limitation. (We should try to make it as small and efficient as possible.)
should be sufficient since we can calculate the length of an ELF (and we are already doing it). |
By the way, here is a bare-bones static AppImage type 2 runtime written in Go: https://github.com/orivej/static-appimage This runtime is using zip rather than squashfs. It has the added benefit that any existing unzip tool should be able to extract it. (Maybe such AppImages should be named Don't use it for production yet since it may be lacking more advanced features like update information, embedded digital signatures, and such. But it shows that it is doable to make a static AppImage runtime using FUSE. |
Depends on the architecture, around 2 MB: When you run |
That other project is doing is really different from us. It's hardly comparable to our runtime. Any sort of size estimation based on that is too imprecise to tell anything useful. Given their runtime is already way larger than ours doesn't really aid your point. A fully statically linked FUSEless runtime would be great. But I don't see how this can be realized while keeping all the features and characteristics of the existing runtime. Writing a runtime in Go is also pretty much a bad idea. It adds way too many uncontrollable dependencies. It's a huge mess. Our runtime is embedded in every AppImage. It needs to be absolutely bullet proof license wise. Ideally, it's licensed as permissively as possible, as legally we cannot even safely assume the the resulting AppImage is not considered a derivative work derived from the runtime. This question hasn't been fully answered for the existing runtime. |
Or maybe a completely different approach can be taken: Provides a modified version of glibc and musl libc that have appimageRuntime embedded into it by modifing functions The The The If the program cannot be compiled to use this Add a header to the program that contains a runtime that decompress the environment including the modified libc to tmpfs and setup environment variables The libc then can have its Edit: I found that the interpreter and rpath of ELF can be changed by NixOS/patchelf, so there is no need to use |
Unzip which files? AppImages are mounted, not extracted. This gives them their speed. |
I was suggesting to throw away fuse and use a compressed tar instead. Extracting a compressed tar won't be a lot slower than squashfuse while fuse adds overhead to application. Every read/mmap of the executable or resource bundled with appimage need to go through fuse, which requires the process to wait for at least 2 context switch instead of just one. |
@probonopd I've done a naive benchmark between squashfuse used in appimage and tmpfs using nvim.appimage
You can see that operations performed on tmpfs is much faster than squashfuse. Edit: The benchmark above test the cold run. The warm run is much faster, but still slower than tmpfs:
|
If I understand it right, it Looks like https://github.com/eth-cscs/spack-batteries-included is providing a solution for this. Should we backport these changes into the AppImage runtime?
Reference: |
For those interested in running AppImages in musl containers like me (namely, those based on Alpine), a solution that works today is to extract the AppImage to the container filesystem while building it (for example, with a If the AppImage was generated with a tool like The idea stated above is also applicable in any scenario in which it is feasible to extract the AppImage in a glibc system before running it on a maybe musl system. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
@mgord9518 I think you can do fancy stuff with cgroups. Browsers (including Electron's runtime), tools like podman and buildah and many more examples show you can perform mounts. We may be able to get rid of FUSE in the future. I'm not an expert on cgroups, though, and I'm relatively sure the solution would add some complexity over just using FUSE (where we don't even need to worry about the filesystem, as this software already exists). I think we should open an issue for that. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
What is the price to pay for static linked lib.? Originally it was 700KB. But you mentioned pulling only necessary items and reducing that. Some would argue that static will not break whole systems like fuse3 does |
Please stop posting off-topic messages. This has been answered in detail in the discussion above. |
You could just have a newer differently named appimage maker and not worry . Have it in beta for a year and see what happens.. I have a small user base and can test |
This might be a language barrier issue... |
@TheAssassin That (using cgroups to mount without root) sounds pretty interesting as a fallback. Assuming the implementation isn't super complex, I wonder if it would be feasible to write a wrapper around it to make the API identical to libFUSE, which could be maintained as a separate project. I'd be happy to do some research, my current knowledge of cgroups is essentially nil but it's cool to hear there are other options |
I am a native speaker of Merikan English but I mostly live abroad. I just lost track of the issue and previous history.. My question is, is the information below actually the idea where "just pieces were pulled and compiled directly" or if the whole fuse2 library was statically linked? Here it is.
|
I also looked at the static release folder and noticed there are releases for each linux OS. Is this for the end user as well or just the user who builds. I'd be happy to try using it, but I don't know what to do. I'm not so familiar and intuitive with today's assumed knowledge. If you give me a command or a series of commands I can run on an ubuntu 18.04 droplet. I'd be happy to test and also ask a few others to test my app. Our flutter app is quite heavy and uses sqlite3 as well. my commands to build are below: |
@bksubhuti the repo you listed is not the runtime being discussed here. The static runtime being talked about here is just the original runtime being linked statically instead of dynamically (at least to the best of my knowledge) |
Just to clarify; When you statically link something, it only uses the symbols relevant to the program, sometimes it is even smaller than using dynamically linked binaries, because a dynamically linked binary makes the LD lookup where the libraries are, which means;
There are LOTS of drawbacks, but those are the most important, read this if interested: https://harmful.cat-v.org/software/dynamic-linking/ I see one possible "fix" for now... AppImages could detect missing libraries in the system prior to running, then pull such libraries from a repo containing the missing libraries compiled against Musl, uClibc or even Glibc if need be, so long as they are made portable appropriately, also, note that using Patchelf one can modify ELFs and change where they look for dependencies or you can also replace their hard dependencies or remove them so that the Dynamic Loader lets you run the program even without loading such dependency. :) |
@xplshn what you talk about sounds very off-topic. This issue is about the application that provides the filesystem for the real application inside the AppImage. The dependencies of the application inside the AppImage are beyond its scope. There is no increased attack surface due to dynamic linking. If you can run code to change LD_LIBRARAY_PATH, you don't need to use this to persuade another application to run your malicious code. You can run your malicious code directly. When application gain privileges through setuid LD_LIBRARY_PATH is ignored. Glibc is the prime example of backward compatibility. It has stuck to the same soname for the past 27 years and provides multiple versions of a symbol to support applications that need the old behavior of a function. Your points about load time and invisible size might also be false. If an application loads libpipewire from outside the AppImage, there is most likely already another application (e.g. pipewire itself) that has this library loaded and the Linux kernel will share pages between these application as long as they remain unchanged. The dynamic loader just has to resolve the symbols, which is usually done lazily. |
Try running something compiled in Ubuntu 10.4 in Ubuntu 22... I cited that webpage because it'd be bad seen if I pasted here a 100 lines long explanation and demonstration of the pitfalls of dynamic linking. If you are interested in learning, read this. |
You could also read this very informative paper: http://www.nth-dimension.org.uk/pub/BTL.pdf - Title: "Breaking the links: Exploiting the linker" |
Tried hdparm, works.
Learning from that page? That's just a collection of anecdotes from ancient UNIX systems.
How do the quirks described in there affect AppImages? The only takeaway I see is that when setting LD_LIBRARY_PATH in an AppImage, one should add This is an attack on people trying to analyze a binary with ldd, not on people who intend to run the binary. |
How it relates to AppImages? Binaries compiled statically are portable when packaged as an AppImage, dynamically linked ones are not, and not only are they not, their pitfalls outweigh the benefits. |
AppImages should run on all Linux Platforms, but currently they don't, this is because it is dynamically linked against glibc.
I tried to run a AppImage on Alpine Linux and it failed because Alpine Linux is build around musl libc instead.
I think AppImages should generally include all necessary dependencies and not some of them. Also adding libc would not increase the resulting size much, depending on the used libc, it may only be from 185k to 8M libc Comparison Chart. And if the binary is also stripped it can also be a much less.
AppImage should do something like this:
The text was updated successfully, but these errors were encountered: