3 ms·
> C++ class vtables are a prime target for heap overflow attacks because they store function pointers on the heap Is that so? The actual pointers should be in
by quotemstr 10y ago
> C++ class vtables are a prime target for heap overflow attacks because they store function pointers on the heap
Is that so? The actual pointers should be in .data. The pointer to the vtable is on the heap (or on the stack, or wherever your object lives). In any case, you should be able to use something like [1] to place constraints on indirect calls, right? There are lots of approaches to double checking function calls that don't involve increasing function pointer size.
Besides, there are lots of bits of code that depend on function- and data-pointer interconvertibility --- people can stuff function pointers in raw void* cookies to callbacks.
Rather than make the effort to make existing code work in this (in practice, new) subset of customary (as opposed to standard) C, wouldn't it be better to just write components in a language that's memory-safe to start with?
[1] https://gcc.gnu.org/wiki/cauldron2012?action=AttachFile&do=get&target=cmtice.pdf https://gcc.gnu.org/wiki/cauldron2012?action=AttachFile&do=g...
- wahern 10y agoI didn't mean to imply that you needed to increase function pointer size or otherwise change pointer representations to add such counter-measures. I mentioned tagged pointers in the ancestor post as a classic justification for restricting conversions between object and function pointers. Restricting void conversions for function pointers is just a pragmatic example where part of the burden of implementing and enforcing new language features can be paid by reference to the C standard and the license it affords. It's loosely related to tagged pointers in that both depend on software not abusing the type system too thoroughly. And as problematic as C's type system is, it's not without rhyme or reason. We don't _need_ to rely on the the C standard in any way, shape, or form to implement counter measures. But the function of a language standard is to be a locus for these things, and to help ease the burden of coordinating and implementing changes, and pushing adoption among a wide and diverse community. As for simply just switching to some other language, I don't think that's even remotely realistic. C isn't going away anytime soon, and neither are people going to stop writing new C code anytime soon. People rave about a language like Rust, but there is no language called "Rust". It's constantly being updated, claimed guarantees regarding compatibility notwithstanding. You simply cannot rely on Rust to create a large and complex ecosystem of software yet, and probably won't for awhile. Conversely, when authors of a language like Go adamantly refuse to evolve the language out of legitimate concern for compatibility, people flip out. Language maintainers are damned if they do, damned if they don't. There's no simple answer or approach. C has survived for as long as it has partly _because_ undefined and unspecified behavior provide space for it to slowly evolve through controlled experimentation. And it will continue doing so (and should do so) on its descent just as it did on its ascent.
- steveklabnik 10y ago> It's constantly being updated, claimed guarantees regarding compatibility notwithstanding. Updated does not mean incompatible. And the community bears this out, in the survey, most people had things Just Work, and for those that had issues, they said the upgrade was incredibly minor. If there's something different than this, I'd love to hear about it: we care deeply about stability.
- wahern 10y agoRegarding the paper you cited, that doesn't really address the core issue and sort of misses the point. 1) The issue isn't just modifying vtables; again, that's just a classic example intended to highlight the issue. Both C and C++ code can and often does juggle pointers to regular functions. 2) The referenced mechanism to protect vtables and calls through vtables _rely_ on the much stricter language semantics regarding method invocations. My entire point was that by enforcing some of the restrictions C already guarantees regarding function pointers, we can more easily implement similar approaches for C and C++ code directly manipulating function pointers. Your example about void pointer cookies for callbacks is perfect. Restricting conversions between function and object pointers makes it easier to add instrumentation to verify integrity. I'm sure it's possible to do that without such limitations, especially with your particular example, but it adds complexity and removes opportunities for optimizing away the cost. Undefined behavior helps compilers improve performance because it simplifies the model compilers require for proving the correctness of generated code. That same dynamic also makes it easier to implement and enforce security constraints. That simpler model can allow the compiler to assume a pointer is valid, but it also permits it to more easily detect and abort the program when it isn't without violating the contract with the programmer. Contrast that with a language that permits you to dynamically update code sections by poking and jumping to memory anywhere. That permits all kinds of fun hacks, but it means the compiler can't enforce any meaningful constraints. There's a huge middle ground between that and a language like Rust or Haskell. I find it odd that people will in the same breath claim that C is a security nightmare and should be abandoned, yet argue to preserve insecure semantics that could be easily removed and in many cases are already disallowed by the standard. That's a harmful cognitive dissonance. To be clear, I'm not accusing you of that; but I find your arguments to be uncomfortably close to that misguided conception of C and the C ecosystem.