4 ms·
Wouldn't it be a lot easier to just use a C compiler that produces memory-safe code? I'm sure someone else has already thought of this, but in case not... All
by radford-neal 5y ago
Wouldn't it be a lot easier to just use a C compiler that produces memory-safe code?
I'm sure someone else has already thought of this, but in case not... All you need to do is represent a pointer by three addresses - the actual pointer, a low bound, and a high bound. Then *p = 0 compiles to code that checks that the pointer is in bounds before storing zero there.
I believe such a compiler would conform to the C standard. Of course, programs that assume that a pointer is 64-bits in size and such won't work. But well-written "application level" programs (eg, a text editor) that have no need for such assumptions should work fine. There would be a performance degradation, of course, but it should be tolerable.
- retrac 5y agoYes, such approaches can be compliant. There's even a few C interpreters. Very popular back in the day for debugging C programs when you didn't have full OS debugging support for breakpoints and etc. Such an approach would be quite suitable for encapsulating untrusted code. There is definitely some major overhead, but I don't see why you couldn't use JIT.
- deleted 5y ago[deleted]
- xavxav 5y agoThere are similar approaches, ie: Checked-C which work surprisingly well. However, I'm not sure that this approach would be expressive enough to handle the edge cases of C craziness and pointer arithmetic. There's more to memory unsafety than writing to unallocated memory, even forcing a write to slightly wrong memory (ie setting `is_admin = true`) can be catastrophic.
- radford-neal 5y agoI think it handles all standards-conforming uses of pointer arithmetic. Even systems-level stuff like coercing an address used for memory-mapped IO may work. For example, struct dev { int a, b; } *p; p = (struct dev *) 0x12345678; should be able to set up p with bounds that allow access only to the a and b fields - eg, producing an error with int *q = (int *) p; q[2] = 0; Of course, it doesn't fix logic errors, such as setting a flag to true that shouldn't be set to true.
- AlotOfReading 5y agoThat's essentially what ASAN is, with some black magic for performance and scope reasons. The problem is that ensuring that your code will detect or catch memory unsafety isn't enough, because the language itself isn't designed to incorporate the implications of that. If you're writing a system messenger for example, you can't just crash unless you want to turn all memory unsafety into a zero-click denial of service.
- radford-neal 5y agoPrograms that would crash when using the memory-safe compiler aren't standards conforming. If you're worried that programs crashing due to bugs can be used for a denial-of-service attack... Well, yes, that is a thing. Low-level OS and device-handling code may need to do something that won't be seen as memory safe, but I expect that for such cases you'd need to do something similarly unsafe (eg, call an assembly-language routine) in any "memory safe" language. I'm not familiar with how ASAN is implemented, but since it doesn't change the number of bytes in a pointer variable, I expect that it either doesn't catch all out-of-bounds accesses or has a much higher (worst case) performance impact than what I outlined.
- AlotOfReading 5y agoI brought up ASAN because it's a real thing that already exists and gets run regularly. The broad details of how ASAN is implemented are best summarized in the original paper [1]. The practical short of it is that there are essentially no false negatives in anything remotely approaching real-world use. A malicious attacker could get around it, but any "better algorithm" would still run into the underlying issue that C doesn't have a way to actually handle detected unsafety and no amount of compiler magic will resolve that. You have to change the code. Whether that's by using another language annotations or through annotations like checked C is an interesting (but separate) discussion in its own right. As for the point that programs with memory unsafety aren't standards conforming; correct but irrelevant. Every nontrivial C program ever written is nonconformant. It's not a matter of "just write better code" at this point. [1] https://www.usenix.org/system/files/conference/atc12/atc12-final39.pdf https://www.usenix.org/system/files/conference/atc12/atc12-f...
- ghusbands 5y agoThat doesn't at all address use of pointers that have since become invalid (via free or function return, say).
- radford-neal 5y agoGood point. There's also the problem of pointers to no-longer-existing local variables. (Though I think it's rare for people to take addresses of local variables in a context where the compiler can't determine that they won't be referenced after they no longer exist.)