7 ms·
Especially for security-critical operations, it is often a good idea to disassemble the relevant sections of the binary and ensure that things are handled appro
by alltakendamned 7y ago
Especially for security-critical operations, it is often a good idea to disassemble the relevant sections of the binary and ensure that things are handled appropriately. Optimizations can result in security controls not working as expected.
Edit: More background:
https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Bewar...
https://people.eecs.berkeley.edu/~dawnsong/papers/The%20Correctness-Security%20Gap%20in%20Compiler%20Optimization_may%202015.pdf https://people.eecs.berkeley.edu/~dawnsong/papers/The%20Corr...
- EmpirePhoenix 7y agoThe more I read stuff like this, the more I come to the conclusion that actual security critial real time software should probably be compiled without any (agressive) optimisations
- gmueckl 7y agoYou will be hard pressed to find a compiler that has a high level safety certification that covers optimizations. Most safety manuals I am aware of explicitly forbid enabling optimizations.
- fuklief 7y agoCompCert ?
- gmueckl 7y agoOkay, it recently did get quite high formal certifications.
- Tomte 7y agoARM Clang does not require optimizations to be switched off, and is certified for SIL3/ASIL4, IIRC. Edit: https://store.developer.arm.com/store/embedded-iot-software-tools/arm-compiler-6-functional-safety https://store.developer.arm.com/store/embedded-iot-software-...
- chrisseaton 7y ago> Optimizations can result in security controls not working as expected. Most likely the security controls weren't correct in the first place, which is pretty bad for a security control.
- alltakendamned 7y agoDifferent compilers produce different binaries, different compiler settings result in different binaries. The reality is that the source code is just an input, and even if it's correct or sensible, the control flow in the produced binary is the only truth on a system. An example is when e.g. zeroing out a buffer containing secrets after usage, this overwrite is stripped by the compiler.
- zimbatm 7y agoOr C doesn't provide the necessary semantic. For example, trying to zero a page before freeing it (because is contains sensitive information) is surprisingly hard. Optimizing compilers will consider that since the memory is not going to be accessed it doesn't need to write the zeroes to it. Look at all the contortions libsodium goes to do coerce all the compilers to do the right thing :) https://github.com/jedisct1/libsodium/blob/a26467874a512de304f6e7e25d98af549963de9f/src/libsodium/sodium/utils.c#L97-L142 https://github.com/jedisct1/libsodium/blob/a26467874a512de30...
- chrisseaton 7y agoWhy don't people write things like that in assembly and call that from C?
- beefhash 7y agoI could imagine that perhaps they want the program to work on architectures other than the ones they can provide and test assembly code for.
- chrisseaton 7y ago
- nils-m-holm 7y agoIf some code does not work as expected, the compiler is broken. Sacrificing correctness for speed is an unfortunate trend. I have written lots of compilers in my life, both toys and "real-world" ones, and I can say with certainty that more than 90% of the bugs appear in the optimizer. Without formal verification or at least very exhaustive testing, optimizations should not be included in compilers. Of course, this is very hard to accomplish in low-level languages.
- viraptor 7y ago> does not work as expected Is this meaningful? Lots of behaviour is logically unexpected by developers, but well described in the standard and implemented correctly in the compiler. If the language gives unexpected results, that's closer to the specification problem than a compiler issue.
- pjmlp 7y agoThe compiler is only broken if the developer took into account the respective language reference, and the compiler fails stardard certification tests.
- CJefferson 7y agoThe problem is, as far as I know, no languages make any promises about values left in physical memory or enforcing code take constant time. Security code cares about these things, so has to make use of a best effort, and checking assembly.
- signa11 7y agowell, for intel-cpu's i am not so sure how it might be possible, given all that is known after the spectre/meltdown fiasco.
- zrm 7y ago> Especially for security-critical operations, it is often a good idea to disassemble the relevant sections of the binary and ensure that things are handled appropriately. The problem there being that a) all code is security-critical if the compiler elides a bounds check or turns it into a buffer overrun or just crashes and allows for DoS, and b) that requires you to inspect the code generated by every compiler that will be used to compile the code, which for published source code could be any compiler in the world, including ones that don't exist yet.
- jancsika 7y agoWouldn't WASM help here? One could choose a popular dev target like Ubuntu LTS and seek to comprehend the relevant optimization behavior for a popular compiler like GCC. Now part b above is reduced to a single environment to generate a binary for all archs where WASM will run.
- marcosdumay 7y agoIf a compiler issues code that elides a bound check or crashes for good source, it is broken. If a compiler optimizes your constant time algorithm into a completely equivalent one with varying time, it did nothing wrong, except on security-critical code.
- zrm 7y agoSuppose you have a function that looks like this: void f(int a[], size_t len) { size_t i=0; while(1) { int val = a[i++]; if(i > len) break; do_something(val); } } You would expect for this code to call do_something() for the first len values in a[], which will be the result on a lot of compilers. But suppose the true length of a[] is exactly equal to len, and the compiler is aware of that. Then when i is greater than or equal to len, reading a[i] is undefined behavior. That means that either the bounds check is always false and can be elided, or it isn't false which means there is undefined behavior that allows the compiler to do anything it wants, and maybe what it wants is to elide the bounds check as an optimization. So it does and the compiled code will keep executing do_something() on every value in memory after a[]. The code is wrong, but not in a way which is intuitively obvious, and not in way that compilers had historically taken advantage of to such deleterious effect. And you can't figure that out by looking at the compiler output for a compiler that doesn't take that optimization.
- yxhuvud 7y agoAnd then someone upgrades their compiler and things break.