I recall gcc3 -> 4. The prevailing "wisdom" was emerge --deep (etc) world ... twice! My laptop was left for around a week trundling through 1500 odd packages. I think I did system first, twice too. I left it running on a glass table in an unheated study, propped up to allow some better airflow.
One of the great things about Gentoo is that a completely fragged system doesn't faze you anymore. Screwed glibc? Never mind. Broken python? lol! Scrambled portage? Hold my beer.
I have a VM running in the attic that got a bit behind. OK it was around eight? years out of date. I ended up putting in a new portage tree under git and reverting it into the past and then winding it forwards after getting the thing up to date at that point in time. It took quite a while. I could have started again but it was fun to do as an exercise.
I still haven’t decided whether or not I should be embarrassed that I mainly bought a 16-core CPU to run Gentoo.
The possible combinations that Gentoo allows looks to me like a sort of Linux immune system in action. Quite a few "unpopular" flags will get used (lol USEd) somewhere by someone that will be more motivated on average to log a bug somewhere.
Gentoo also got the console shell look (colours, fonts etc) right way before any other distro. It's copied widely.
[0] https://www.dropbox.com/s/w1zlftin1cojkhr/kernel_compile.mov...
I think that you could do this quite well on NixOS, and I'm now intrigued to try to rig up a proof-of-concept when I can find the time.
Side-effect: Does not work for libraries without a significantly more complex wrapper that certainly could not work for all libraries. Though, you could re-order the objects within a static library fairly easily.
Truthfully though you're right, using typical linkers, this would be pretty slow; at least a few seconds for large binaries, to minutes for things as large as web browsers. However, for many binaries, linking can be done much faster; mold claims to be only 50% the runtime of using `cp` on the object files, which is fast enough to even re-link Firefox on-the-fly without it being unusable.
You could imagine writing a linker specifically for this case, that encodes information about the object files directly into the resulting bundle.
I’m not saying it’s perfect but it seems like a reasonable defense for binary distribution. As someone who used to run Gentoo, I’d say most people are in favor of the faster times to install a new package.
EDIT: extending this idea further, I wonder if compilers can’t offer a random seed to supply that causes a random layout of the sections within a built execution so that even statically linked binaries benefit from this.
Finally, the main problem with this idea is that you can't audit malware because there's no way to maintain a source of truth about what the binary on a given system should be. Distributing randomly linked copies solves that because you can have a deterministic mapping based on machine characteristics (you do have to keep this hash secure but it's feasible). You'd basically be maintaining N copies of your distro with randomly built binaries with the user being given a random one to install.
And to be clear, my better idea is to do this at the compiler level so that you randomize the relative location of functions. That way it's impossible to find any gadget to grab onto and you have to get information leakage from the machine you're attacking & this information leakage has to be repeated for each machine you want to compromise.
For example.
I wonder if there's a way to do just-in-time random relinking such that the performance cost is low, but the security benefit is still strong.
Just-in-time gets you reproducible builds, and also addresses the "local attackers who can read the binary or library" problem.
There would be a performance cost in terms of startup time, but since the number of possible permutations is a factorial function of the number of possible linking orders, it seems like even a very coarse-grained random relinking can go a long way.
You could accomplish this by doing static analysis of a binary to generate a file full of hints for ways to rewrite the binary such that its behavior is provably equivalent to the original. Then there could be a wrapper (perhaps at the shell or OS level) which uses the hints to randomly relink on the fly just prior to execution.
Another advantage is that this approach should be feasible on an OS like Ubuntu where everything is precompiled.
However the static analysis part could be a little tricky? I'm not familiar with the state of the art in static analysis of compiled binaries.
Performance-sensitive users could be given a way to turn the feature off, in cases where fast startup time was more important than security.
The biggest benefit seems to be in making it infeasible/dangerous for a malicious actor to distribute binary versions containing different behavior from the published source.
On a local machine, when and with what would you compare your binaries?
Reproducible builds verify the source code and build process (including options) were the same. Not sure how important each aspect is.
Also, if for some reason you rebuild a dependency, you'll need to relink everything that depends on that. This could get messy, but it's still interesting.
Other than this issue (which may well be a large / unsolvable one), I wonder what other disadvantages to this approach there might be. Does this hack have any potential for a Gentoo profile or mainlining?
Is dynamic linking in Unix world truly runtime-only (a-la "GetLibrary" / "GetProcAddress")?
But I also don't think that this would be a problem on Windows. After all, you can generally replace DLLs with entirely different versions and you'll be fine as long as all the required symbols are present and ABI-compatible.
The main difference between ELF and PE dynamic linking is that with PE you have a list of required symbols along with the libraries to load those symbols from while with ELF you have a list of required libraries and a list of required symbols but not information recorded about which symbols should come from which libraries.
There are all kinds of things we're doing (e.g. rewriting things in memory-safe languages) to make it less likely for an attacker to become able to control a jump to somewhere, however, we don't expect to fully succeed any time soon, and this is defense in depth against cases when attackers once again do find a way to control transfer to some arbitrary gadget.
> Return-oriented programming (ROP) is a computer security exploit technique that allows an attacker to execute code in the presence of security defenses[1][2] such as executable space protection and code signing.[3]
> In this technique, an attacker gains control of the call stack to hijack program control flow and then executes carefully chosen machine instruction sequences that are already present in the machine's memory, called "gadgets".[4][nb 1] Each gadget typically ends in a return instruction and is located in a subroutine within the existing program and/or shared library code.[nb 1] Chained together, these gadgets allow an attacker to perform arbitrary operations on a machine employing defenses that thwart simpler attacks.