5 ms·
Retrofitting Temporal Memory Safety on C++
- spockz 4y agoReading through the implementation(s) it seems like they are halfway to implementing a garbage collector already. Interestingly, the mention MTE. Could that be used in GC as well? To in place mark the generation of an object without having to copy it maybe? > MTE (Memory Tagging Extension) is a new extension on the ARM v8.5A architecture that helps with detecting errors in software memory use.
- verdagon 4y agoI'm glad MTE is catching on, I think it's the future for C and C++'s memory safety story. Hopefully intel comes out with something for it soon too! Vale is taking a similar approach at the language level, by putting a generation at the top of every allocation [0] and it's proving pretty fast. It's also proving more accurate, since its generations are larger; MTE uses 4-8 bits, and generational references use 32-64. It would be cool to see C++ itself embrace generational references in the standard library (perhaps via a std::gen_ptr?), it would give us some memory safety without needing to use the heap, which would be a big improvement over modern C++'s encouragement of std::shared_ptr. [0] https://verdagon.dev/blog/generational-references https://verdagon.dev/blog/generational-references
- dataflow 4y agoConfused, why are generational references being compared with reference counting? One is intended for shared ownership, the other is intended for memory safety. They seem completely orthogonal/unrelated to each other; they look like complements rather than competitors. I'd also be interested in knowing whether they compared against a single-threaded reference counter or a multi-threaded reference counter. The atomics in a thread-safe RC severely impact performance; I hope they didn't compare that against GR.
- verdagon 4y agoI see RC and GR as two methods of ensuring memory safety: one that extends the life of an object, and one that asserts the object is still alive. They both protect us from vulnerabilities and UB, but with different approaches. Yep, it was compared to single-threaded RC for fairness.
- dataflow 4y agoRC isn't there to protect you from vulnerabilities though. It's there to let you share ownership. There's some side benefits in that regard, but the latter can't substitute for the former except in cases where the former is unnecessary to begin with. As an analogy, imagine comparing a safety knife, a kitchen knife, and (a safe version of?) an axe with each other, and touting how much lighter/safer/whatever the first one is compared to the others. Of course that's correct, and you can make the comparison, but there isn't (or at least shouldn't be?) a huge market of people out there using an axe in the kitchen who could've just used a knife instead, so it's a weird choice of a market to sell an otherwise-great product to. (And thanks for clarifying the single-threaded aspect.)
- MaulingMonkey 4y ago> It's there to let you share ownership. And ownership - shared or otherwise - is a great way to eliminate the use-after-free bugs that would otherwise be caused by naively attempting to create a non-owning, borrowing reference to something else. It is in fact the primary use of refcounted pointers in many C++ codebases I've worked with - perhaps with a single std::shared_ptr and several std::weak_ptr s, meaning you're not really sharing ownership per se despite the refcounted nature - or perhaps with multiple std::shared_ptr s out of sheer laziness. I've had my share of C++ code reviews dinged for perfectly valid code containing non-owning references, simply because such code could be fragile and prone to use-after-free heisenbugs in the face of refactoring, and my reviewers were right to do so. Things are safer in Rust - you can play fast and loose with storing &str s and rely on the borrow checker to catch your mistakes - but playing fast and loose with storing std::string_view s will lead to slipped release dates as you chase 11th hour heisenbugs. Store a std::string or std::shared_ptr<std::string> instead, even if it's "unnecessary". > there isn't (or at least shouldn't be?) a huge market of people out there using an axe in the kitchen who could've just used a knife instead Some of us are out camping in the wilderness that is C++ gamedev, and want to make kindling. Do I need to pack a whole axe, or would a knife I need for other reasons anyways make do and lighten my backpack? And even if that's a small market, it's still entirely a reasonable market, and for advertizing to that audience - however niche - comparing said tools would be an entirely justifyable approach to selling the product. I also think you underestimate the size of said market.
- pjmlp 4y agoMe too, pity that is something that only Solaris SPARC has had in production at scale for the last decade. C++ might be getting hazardous pointers thought, although most likely they will only make it to C++26.
- cyber_kinetist 4y agoYou can certainly implement generational pointers in C++ code without further language-level support (given that you’re using your own handmade object pools). Don’t know if this can get to the STL though, since it requires a malloc (or object pool) implementation tailor-made for it. I’ve implemented this in my current C++ game engine, and works wondrously well. You can even pack that generational index in the unused 16-bits of a 64-bit pointer, if you have further constraints (like a C library only allowing void* as userdata)!
- rfoo 4y ago> they are halfway to implementing a garbage collector already This is a common misunderstanding. The key insight of this work is actually "morphing dangling pointers into memory leaks is WAY EASIER than automatic memory management (a.l.a. GC or refcount)". As long as we still do memory management by hand, we are able to avoid most of the complexity of GC (or defects of RC) and convert one bug class to another without much overhead. Yes, this work can't eliminate bugs in your code (unlike, say, Rust, or other GC languages). But some bugs matter more than others, and, it works on existing code, it makes significant contribution to security without rewriting the world with Rust or JavaScript.
- pjmlp 4y agoYeah, that's the thing, there are some domains that aren't going to ever move away from C and C++ dynamic duo no matter what, at least until something like quantum takes over (how many decades still?), so any help to improve their security is welcomed.
- tialaramex 4y ago> aren't going to ever move away from C and C++ dynamic duo no matter what Forever is a long time. > any help to improve their security is welcomed. I'm less certain of this than you are, after decades of watching "We may not have really solved the problem, but this helps 99%" almost immediately followed by "Oh, our adversaries apparently aren't idiots and so our 99% mitigation did not actually reduce our burden significantly". For domains where you don't have security considerations, this is fine. If you make single player video games a mitigation which means you have 4 crash bugs to fix in your game instead of 400 is excellent news. The bugs aren't trying to catch you, so mitigations are effective. But in security your attackers will put no effort into cutting through your expensive thick steel reinforced concrete wall, and focus their effort instead on picking the $4 padlock on your gate in that wall or vaulting over the wall, or just say they're a package delivery and drive in past your guards or a dozen other things you never thought of.
- pjmlp 4y ago
- donkarma 4y agowell you don't actually have to allocate memory to keep it blocked do you? you can just have a page that isn't present and isn't actually backed by physical memory
- petmon 4y agoIt is possible to allocate every object in its own page. In practice this is useful as a debugging tool but far too expensive for day-to-day operation.
- deleted 4y ago[deleted]
- retroshit 4y agoMaybe the real issue to solve is to reduce the page size to make this a feasible approach to mitigation. Hardware designers have made this choice, but, does it really need to be 4096 bytes? With a much smaller size, say, 128 bytes, almost every new allocation could be put into a new page at a new address, with one or more guard pages behind it. Yes, it would increase the size of the page tables, and the lookup time, but maybe it's worth it.
- saagarjha 4y ago4K pages already tend to perform worse than, say, 16K ones.
- danuker 4y agoI wonder, have they tried TLA+? This sounds like a good fit for designing a memory safety algorithm. https://lamport.azurewebsites.net/tla/high-level-view.html https://lamport.azurewebsites.net/tla/high-level-view.html
- Jyaif 4y agoThey are not trying to design an algorithm, they are trying to make an entire browser safer.
- fulafel 4y agoPreviously: https://news.ycombinator.com/item?id=31558796 https://news.ycombinator.com/item?id=31558796
- einpoklum 4y agoThe example in the article is something that, for the past decade or so, has been discouraged more and more strongly and is almost entirely unnecessary: Manually allocating and freeing memory (plus letting a variable outlive its use and be used for something else). See: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-resource https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines... and specifically, guidelines R.10, R.1 and R.3. See also this answer: https://stackoverflow.com/a/48046118/1593077 https://stackoverflow.com/a/48046118/1593077 to the question of why C++ doesn't have a garbage collector. To be more concrete, the example would be written today as: { auto foo = std::make_unique<Foo>(); } foo->Process(); the last line won't compile - foo is gone already; and if you moved it inside the block scope, then foo would be valid. Now, the guidelines I mentioned can also be partially statically-checked, and some IDEs have begun to do so, so that the original snippet would give you at least 3 or 4 warnings. Interestingly, this will be true in a larger codebase as well, despite the claim in the article that > UAFs are often hard to spot in larger codebases where ownership of objects is transferred between various components. and that's because if you adhere to the coding guidelines _locally_, but throughout your application, including also I.11 about ownership transfer, then you can never free a pointer you don't own (unless you explicitly decide to do so), and you can even avoid dereferencing null pointers. Yes, I meant what I said: You can simply avoid ever dereferencing a null pointer. All of this is supported by static analysis, so you absolutely don't need to be a virtuous and skilled coder to avoid such conundrums. --- There's a caveat though: If some of the code is not under your control, but is rather some 3rd-party library or header, then what I said only applies if you properly "sanitize" at the boundaries of your well-checked.
- pebal 4y ago> See also this answer: > https://stackoverflow.com/a/48046118/1593077 https://stackoverflow.com/a/48046118/1593077 > to the question of why C++ doesn't have a garbage collector. The shared_ptr is an optional garbage collection based on reference counting. Slow, leaky, deterministic (that's an advantage and a disadvantage). C++ needs optional garbage collection if needs shared_ptr.
- 4y ago
- fefe23 4y agoI find it quite discouraging to see how they basically give up trying to construct code without security flaws, and instead hope for some magic hardware mitigation to rescue them. Investing in mitigations is not free. The time and effort it takes to develop them is not available for constructing bug-free code. And once you have them, there will be even less reason to construct bug-free code. After all the magic hardware-assisted mitigations will save us now. We've been through this before multiple times now. Someone will find a way to exploit around your mitigation, and you will then double down and come up with the next mitigation that will also be circumvented eventually. Personally I believe that "retrofitting" security can never work well. Security must be constructed, not retrofitted. Why is this example code using raw allocations? Construct secure code by using containers, not manual allocations. This has been the recommendation for years now.
- charcircuit 4y agoThey haven't given up in trying. Trying is just not enough. The system should degrade into a state which is still safe when a memory bug happens.
- pjmlp 4y agoGoogle C++ guidelines is well know among the community for not being the best in what concerns C++ good practices, only C++ outsiders look at it as something of value. Something like C++ Core Guidelines, which include your remarks, is what should be looked into. Naturally Chrome follows Google's C++ guidelines, so.
- johannes1234321 4y agoright, it is nitworthy that Google guidlines are soecific to a code base within an organisation. There is quite some value in reading the rules and reasoning, but it isn't Gospel by any means.
- saagarjha 4y agoGoogle's C++ guidelines have almost nothing to do with Chrome's memory safety efforts. The concerns brought up in the article are relevant to other browsers too.
- PointyFluff 4y ago