4 ms·
Machine code does not require granting anything. The presence of a shared filesystem is a artifact of the OS, not any sort of inherent requirement. The actual
by Veserv 3y ago
Machine code does not require granting anything. The presence of a shared filesystem is a artifact of the OS, not any sort of inherent requirement.
The actual effect of machine code being opaque is actually "beneficial" to security as only very coarse protection boundaries are even expressible. You must coarsely separate your code to get protection which comes at a potential cost of performance. The advantage of non-machine code is that you can express very fine protection boundaries which reduces the performance cost of protection, but at the cost of more complex analysis.
Ensuring protection boundaries in the presence of coarsely separated code is strictly easier. You have strictly less ways to escape. Any coarsely separated code can be trivially transplanted onto a system enabling finer protection since they can just not use the finer protection. By the same logic, any protection boundary that can not protect coarsely separated code has no hope of enforcing protection on finer separations.
That the kernel designers of Linux, Windows, iOS, etc. can not even ensure protection in general when the systems are coarsely separated means they certainly can not be trusted to ensure protection in general when things are more finely separated. If they could, fixing their existing coarse protection boundary would be a trivial consequence. About the only theoretical reason why this might not apply to eBPF is that it is not "general" in that many programs are not expressible, but that is not sound, positive evidence of suitability where as the historical track record of protection in all commercial operating systems is abysmal to say the least. Claims of finally achieving security (on a strictly harder variant of the problem) are extraordinary claims and extraordinary claims demand extraordinary evidence.
- James_K 3y ago> Machine code does not require granting anything It requires that you actually run software on your CPU before you can tell if it's malicious or not, by which point it is often too late to do anything about it. Almost all malware that exists does so thanks to this fact.
- Veserv 3y agoMachine code running in unprivileged mode with no shared memory and where syscalls auto-fail can do nothing malicious. Such a system is even more locked down than a Java program (when it does not also rely on hardware isolation) with no access to the filesystem class. To break the JVM protection boundary and access the filesystem only requires finding a JVM runtime breach or a vulnerable library to access the otherwise inaccessible filesystem class. To break the hardware isolation requires finding a hardware bug in the protection boundary which is vastly less common to the point where I am not even sure there has even been a hardware cross-privilege data integrity bug in over a decade. The examples you keep providing are not apples-to-apples as you keep comparing locked down source level programs against ensembles of machine code programs plus unrestricted access to a insecure operating system runtime. Every error you highlight in the machine-code system is actually a operating system/runtime problem, not a problem with machine code as a software delivery mechanism.
- James_K 3y ago> To break the JVM protection boundary and access the filesystem only requires finding a JVM runtime breach or a vulnerable library to access the otherwise inaccessible filesystem class. CPUs also have bugs in them, and those are much harder to patch. Practically speaking, I think it is more likely for a CPU bug to be found than for a Java program to do what you describe here. The prospect of a vulnerable library seems the most likely avenue of attack, but under my proposed system, not even libraries would have access the the file-system. > Machine code running in unprivileged mode with no shared memory and where syscalls auto-fail can do nothing malicious I'm fairly sure it can do nothing, so while that technically satisfies the objective, I don't think it really matches the spirit of the assignment. Then if you want to re-introduce the file-system to such a process, your options are much more complex than they would be in the Java case. You can write some Java code that doesn't interact with the file system at all, then pass objects representing files (i.e. streams) into it. The result is absolute fine-grained control over what the process can access because it is not relying on any kind of shared state. All the information it uses is passed into it. This is much easier than attempting to containerise an existing executable, which requires support from the operating system (and also technically support from the hardware). My core point here is that the software solution to security simply works better than a hardware one. It seems to me that pure functions provide the ultimate form of containerisation, and do so in an immediately intuitive way.
- Veserv 3y agoCPU bugs are extremely rare compared to software bugs. Like, millions to billions of times less likely. CPU bugs are news, software bugs are Tuesday. You might as well argue that a steel box protects its contents no better than a cardboard box because who knows, maybe a meteor will land on the box so steel is no better than cardboard. You can not just dismiss factors of millions on feelings. Again, you are talking about machine code plus OS ensembles. The machine code can not access a filesystem or files unless the OS allows it just like your Java example. You can pass precisely a file or stream to your program if your OS supports such a notion. Even Linux, as insecure as it is, can express your notion of streams with pipes to input files. The insecurity of machine code on Linux is an artifact of OS design providing things like an innate right for processes to access a file system, not anything to do with the machine code. Containerisation does not provide a security boundary and is difficult because the OS design and the programs targeting that OS API are not designed to operate in that way. Such a program would not operate correctly in your Java model either because the program only functions when there is a global filesystem conforming to the implicit requirements of the programs. As soon as you modify that program to operate correctly according to your model it then becomes trivial to precisely provide the same dependencies to the machine code variant as long as your OS supports such abstractions. Every single abstraction you are describing can be expressed as a OS concept at the cost of performance. An argument can be made that the performance impacts are too high, so commercial designs sacrifice protection and that by supporting language-level protections you recover enough performance to enable secure designs. But, in that case you would expect proof-of-concepts demonstrating feasibility and proven protection in the equivalent, but non-performant model, and only then optimizing the performance by leveraging language-level protections. Until that is done there is precisely zero reason to believe any language-level protections are robust. To say it again, language-level protections are strictly more powerful than OS/machine code level protections. If you can prove them then you can trivially prove any OS level protection by jus allowing less fine separation. If you can not demonstrate weaker results like a unhackable OS design, then you do not have a robust language-level solution. It is like proving P != NP without proving something easier like P != PSPACE. Language level solutions would be awesome, but they are not a stepping stone to protection, they are the end result once you have already mastered security.