One (non-generalizable) solution is to avoid using writable function pointers, to help maintain control flow integrity.
For example, in libopus we have platform-specific SIMD optimizations that are chosen at runtime.
The common solution is just to have a function pointer that points to the accelerated routine. But there are only a limited number of choices for each routine. So we put pointers to them in a static const array, which gets stored on a read-only page. Then instead of storing a pointer, we just store an index into this array. This index is chosen based on the available instruction sets detected at runtime. This lets us use the same index for every accelerated function.
Then, we pad the array size out to a power of two. To call an accelerated function, we mask off the upper bits of the index. Even if the index gets corrupted, we still call one of the functions in the array. So there's only so much damage it can do.
Obviously this doesn't apply if the set of functions you need to call is open-ended (e.g., a callback, like nginx was using). But it seems like a good pattern to follow when it applies.
I don’t think they actually found an exploit for Nginx: they state that “we assume such a memory bug exists”, rather than saying outright what bug they exploited. It almost sounds like they planted a bug and then exploited it in a binary that was hardened (using this non-standard hardening technique, i.e. not exploiting a binary that was ever used in a real deployment of Nginx).
I always thought this is example of basic buffer overflow in C - what's different with this example? If somebody asked me to write buffer overflow example in C I'd write something that looks exactly like this, this must have been known for years, what am I missing here?
No, it's more than that. While that struct might be a generic overrun, the question is how you check bounds on filling buf. The paper is discussing that schemes to constrain buffer size using a global hardware or software address-based size constraints ("low-fat") as bounds checks are less safe than claimed.
That will never happen with UNIX based OSes around, so what we really need, as much as I would like to nuke C, is to have developers accept some variant of "Safe C".
Solaris has it thanks to SPARC tagged memory.
Problem is when would we ever get it as mainstream, always enabled, in other platforms.
I mean, sure, fat pointers are the "right" solution, but the point of "low-fat" pointers in the original paper [1] is for greater compatibility with existing C code, including at the ABI level. Adding fat pointers to C (which is what your proposal is) is incompatible with those goals. Page 2 of the paper discusses this.
I still think that the Deputy's project (from UC Berkeley) approach (https://barnowl.org/research/pubs/07-hotos-linux.pdf) is the most practical for adding bounds checks to existing C code. Basically annotate functions and data structures with the, usually available, bounds information, e.g.:
f(int *a, int n) -> f(int *count(n) a, int n);
or
struct { int len; int *values; }
->
struct { int len; int *count(len) values; }
And other annotations for unions, etc.
This actually works quite well in practice - the cited paper involved us applying these annotations to a complete, bootable linux kernel. You do have to be willing to tolerate limited code changes and a few "trust me" casts
Disclaimer: I worked on the overall project, though not significantly on the Deputy part.
I agree with the framing here, and I agree a small bit of syntax would go a long way.
FWIW, you will see a class called StringPiece copied all over a lot of open source Google projects, including Chrome, Ninja, RE2, probably Android, etc.
That is essentially a pointer-length pair. I agree it would be nice to have a dedicated syntax for it, and not a class that everybody has to copy around.
C++ 17 names this string_view (long overdue!)
I just Googled and apparently there was a problem with array_view ? That's a shame.
How does this fix it? It automatically includes a size_t with all array types but does it also rely on the programmer setting the size_t value correctly? If so there will be the same problems. It seems like this is exactly the same as just passing a size argument along with all array-type arguments, which is already common practice.
> In this paper, we have illustrated a new type of attack that can bypass bounds checking spatial memory safety techniques that protect allocations.
No they have not. Bounds checking only outer structs doesnt do bounds checks on inner buffers.
They haven't analyzed traditional bounds checks in C: asan, fortify, c11 Annex K, Intel MPX, only a broken application, which does none of these, and uses a wrong bounds check.
tl;dr: the authors overflow from one struct field into another struct field, thus avoiding any allocation protection scheme that protects individual heap allocations from each other. Most of the paper is just spent implementing basic ROP-style gadget hopping.
I’m not actually clear on what’s new or novel here. Allocation protection mechanisms exist to prevent one allocated block from overflowing into another allocated block. They usually don’t protect allocated blocks from themselves.
[+] [-] Animats|7 years ago|reply
We've got to get more code out of C and into something with subscript checking. None of the kludges for fixing C work.
[+] [-] derf_|7 years ago|reply
For example, in libopus we have platform-specific SIMD optimizations that are chosen at runtime.
The common solution is just to have a function pointer that points to the accelerated routine. But there are only a limited number of choices for each routine. So we put pointers to them in a static const array, which gets stored on a read-only page. Then instead of storing a pointer, we just store an index into this array. This index is chosen based on the available instruction sets detected at runtime. This lets us use the same index for every accelerated function.
Then, we pad the array size out to a power of two. To call an accelerated function, we mask off the upper bits of the index. Even if the index gets corrupted, we still call one of the functions in the array. So there's only so much damage it can do.
Obviously this doesn't apply if the set of functions you need to call is open-ended (e.g., a callback, like nginx was using). But it seems like a good pattern to follow when it applies.
[+] [-] nneonneo|7 years ago|reply
[+] [-] mirekrusin|7 years ago|reply
[+] [-] kevin_thibedeau|7 years ago|reply
[+] [-] lvs|7 years ago|reply
[+] [-] kragen|7 years ago|reply
[+] [-] pjmlp|7 years ago|reply
Solaris has it thanks to SPARC tagged memory.
Problem is when would we ever get it as mainstream, always enabled, in other platforms.
[+] [-] dataking|7 years ago|reply
shameless plug: https://www.c2rust.com
[+] [-] ElBarto|7 years ago|reply
The other thing to do is to enforce checks on array indices.
[+] [-] ndesaulniers|7 years ago|reply
Turn on ASAN in production. HWASAN may make that more palatable.
[+] [-] rawoke083600|7 years ago|reply
[+] [-] writepub|7 years ago|reply
typedef struct {
[+] [-] unknown|7 years ago|reply
[deleted]
[+] [-] amelius|7 years ago|reply
[+] [-] WalterBright|7 years ago|reply
https://www.digitalmars.com/articles/b44.html
It's simple and it works, we've got 15 years experience with this in D.
[+] [-] pcwalton|7 years ago|reply
[1]: https://www.comp.nus.edu.sg/~gregory/papers/ndss17stack.pdf
[+] [-] davidgay|7 years ago|reply
This actually works quite well in practice - the cited paper involved us applying these annotations to a complete, bootable linux kernel. You do have to be willing to tolerate limited code changes and a few "trust me" casts
Disclaimer: I worked on the overall project, though not significantly on the Deputy part.
[+] [-] chubot|7 years ago|reply
FWIW, you will see a class called StringPiece copied all over a lot of open source Google projects, including Chrome, Ninja, RE2, probably Android, etc.
That is essentially a pointer-length pair. I agree it would be nice to have a dedicated syntax for it, and not a class that everybody has to copy around.
C++ 17 names this string_view (long overdue!)
I just Googled and apparently there was a problem with array_view ? That's a shame.
https://www.reddit.com/r/cpp/comments/5ya5pe/whatever_happen...
[+] [-] saagarjha|7 years ago|reply
[+] [-] krferriter|7 years ago|reply
[+] [-] baybal2|7 years ago|reply
[+] [-] xvilka|7 years ago|reply
[+] [-] carlmr|7 years ago|reply
[+] [-] rurban|7 years ago|reply
No they have not. Bounds checking only outer structs doesnt do bounds checks on inner buffers. They haven't analyzed traditional bounds checks in C: asan, fortify, c11 Annex K, Intel MPX, only a broken application, which does none of these, and uses a wrong bounds check.
[+] [-] srfilipek|7 years ago|reply
I'm more interested in seeing how CPI and CPS pans out in the end:
https://dslab.epfl.ch/pubs/cpi.pdf
[+] [-] unknown|7 years ago|reply
[deleted]
[+] [-] nneonneo|7 years ago|reply
I’m not actually clear on what’s new or novel here. Allocation protection mechanisms exist to prevent one allocated block from overflowing into another allocated block. They usually don’t protect allocated blocks from themselves.
[+] [-] bnastic|7 years ago|reply
[+] [-] acomjean|7 years ago|reply
I suspect the circular never ending problem song might not have been what they were going for (or maybe it was).
https://en.wikipedia.org/wiki/There%27s_a_Hole_in_My_Bucket