8 ms·
If you're considering switching to Rust for code/memory safety reasons, SaferCPlusPlus[1] may be an easier/cheaper/low risk option. It allows you to add memory
by duneroadrunner 10y ago
If you're considering switching to Rust for code/memory safety reasons, SaferCPlusPlus[1] may be an easier/cheaper/low risk option. It allows you to add memory safety to your existing code base in a completely incremental way, with no dependency risk. (At the moment, standard library support is required though.)
[1] https://github.com/duneroadrunner/SaferCPlusPlus https://github.com/duneroadrunner/SaferCPlusPlus
- pcwalton 10y ago"Safer C++", like all C++ template libraries, is not memory safe.
- duneroadrunner 10y agoAre you confusing SaferCPlusPlus with a different library? SaferCPlusPlus is a new library that makes it practical to stick to a memory safe subset of C++ (i.e. no native pointers, no native arrays, no std::array<>, no std::vector<>, etc.). Using the SaferCPlusPlus library to replace all uses of C++'s unsafe elements does result in code that is as memory safe as Rust, or any other modern language. The main shortcoming at the moment is that it doesn't yet provide memory safe replacements for all of the standard library's unsafe elements, just the most commonly used ones.
- leshow 10y agoCorrect me if I'm wrong, but it looks like this just provides some 'safe' alternatives to unsafe C++ things. It's still up to the diligence of the programmer to not use those things and nothing is getting statically verified. By contrast, when I write Rust, memory safety (and type safety) are verified by the compiler.
- braveo 10y agoSometimes I think Rust people lose the forest for the trees. The end goal isn't for the compiler to verify the safety, the end goal is for the software itself to be safe in a way that's cheaper. It doesn't really matter if they both end up at the same place, which is safe software.
- pcwalton 10y ago> Sometimes I think Rust people lose the forest for the trees. The end goal isn't for the compiler to verify the safety, the end goal is for the software itself to be safe in a way that's cheaper. I don't care if the software is verified via libraries or compilers. The problem is that C++ verifiers don't work.
- staticassertion 10y agoWell... how do you know the code is safe otherwise? Exhaustive tesitng is unreasonable. How do you ensure that you have achieved the end goal of safe software?
- dbaupp 10y agoYou're 100% correct that the end goal is safe software in a way that's practical to achieve. However, having the computer check one's code is generally regarded as a great way (even the best way) to do this: NASA's JPL doesn't accidentally recommend[0] turning on all compiler warnings and using static analysis tools, and it seems a little unlikely that most major tech companies would be spending millions on static analysers and statically-typed languages if they didn't think it helped them write correct code. [0]: https://en.wikipedia.org/wiki/The_Power_of_10:_Rules_for_Developing_Safety-Critical_Code https://en.wikipedia.org/wiki/The_Power_of_10:_Rules_for_Dev...
- braveo 10y agoSure, but C++ also has tools to do this checking in an automated way.
- MaulingMonkey 10y ago> It doesn't really matter if they both end up at the same place, which is safe software. I think the contention is that, unless you're applying NASA style rigor, you don't end up in the same place without verifying the safety automatically, because in practice it's too expensive to verify the safety manually (without getting squeezed out of the space by your competitors.) SaferCPlusPlus's goals are noble, but approaching the problem with a library-only solution is problematic. None of the huge swaths of legacy and third party code I'd like to sanitize uses it - and a large scale rewrite to 'fix' that may very well introduce more bugs than it fixes. A library cannot 'fix' fundamental language constructs either, short of telling you to please remember to perfectly avoid those language constructs even if you're very very used to them. Frankly, I'm skeptical of how useful I'd find SaferCPlusPlus even for new projects - especially when modern SC++L implementations already have a lot of error checking code built into them as well, at least for debug builds. Meanwhile, I already credit these to saving me at least a month of debugging time: http://clang.llvm.org/docs/ThreadSafetyAnalysis.html http://clang.llvm.org/docs/ThreadSafetyAnalysis.html I'm interested in Rust because it takes the same approach to securing code from bugs as seems to help a lot when I apply it to C++: Static analysis and annotations, designs to make edge cases impossible to ignore, and where static analysis cannot perfectly find all problems, let it error out reliably at runtime instead of randomly corrupting memory unless I really really really mean it.
- duneroadrunner 10y agoThat's right. SaferCPlusPlus is not complete and does not yet include a static verifier/checker. Without a static verifier, memory safety is not guaranteed, just dramatically improved. And for many cases where there is a large investment in an existing code base, this might still be a more expedient solution. Even if only an interim one. For example, I would estimate that, with concerted effort, it would take a matter of weeks to "port" the existing Firefox C++ code base to SaferCPlusPlus. Presumably this would dramatically reduce "remote execution", and other memory bugs while we wait for the Rust implementation. In cases where guaranteed memory safety is desired, you might think of it this way: In Rust, the static checker is built into the compiler. In C++, static checkers/analyzers are separate tools. You could choose to require that your C++ code must be verified to be safe by a static analyzer of your choosing. In C++, it can be difficult/inconvenient to write non-trivial code that fully appeases the static analyzer, just like in Rust. You can use SaferCPlusPlus to make it easier to fully appease the static analyzer (like the Rust language does). I should also mention "Ironclad C++". It's similar in function to SaferCPlusPlus, but it uses garbage collection (where SaferCPlusPlus does not). It does include a static verifier/enforcer. As a fan of "memory safety without using GC", I'm rooting for Rust. But I think the idea of achieving memory safety in C++ can be too quickly dismissed.
- pcwalton 10y ago> In C++, static checkers/analyzers are separate tools. You could choose to require that your C++ code must be verified to be safe by a static analyzer of your choosing. The problem is that, in C++, there is no such static checker in existence (except ones with GC).
- duneroadrunner 10y agoWell, like I said in the other comment, you guys could fix that by unbundling the static checker in the Rust compiler and making it applicable to (a subset of) C++ code as well :) So then would you agree with the notion that (a practical subset of) C++ combined with a static analyzer could be just as safe and fast as Rust if, hypothetically, there existed an enthusiastic community comparable to Rust's? Or are there intrinsic technical issues? Or syntax issues? Also, let me throw this notion at you: Rather than disallow code that can't be verified to be (memory) safe, the compiler could instead inject runtime checks that would be optimized out using the same analysis that the static checker uses. That is, instead of requiring that the code be fast and safe or it won't compile, it becomes: If your code is not clearly, intrinsically safe then it will have runtime checks that will slow it down. And the compiler could list any runtime checks that it wasn't able to optimize out. The reason I suggest this is that memory safety is just the enforcement of certain invariants. There's no reason why we couldn't let the programmer define additional, application specific invariants and have the build process treat them the same way it treats memory access invariants. So for example, when a user defines a class, it could have a standard member function called "assert_object_invariants()" or something, that the programmer can define. Then anytime a (non-const?) member function is called, the compiler can insert runtime asserts at the beginning and end of the member function call. And again the compiler can tell you when those runtime asserts aren't optimized out. Wouldn't that make sense? I haven't really thought it through.
- pcwalton 10y agoCreate a vector. Push an element onto it. Take a reference to that element with operator[]. Clear the vector. Call a method on that dangling reference. Create an object on the stack. Return a reference to that object. Call a method on that reference. Create a vector. Push an element onto it. Call a method on that element that clears the vector and then calls another virtual method on itself, via the this pointer. Accidentally share a vector between threads. Race push_back() and remove(). Etc. etc. We didn't implement lifetimes for no reason. Additionally, the pointer registration mechanism that that library uses has a runtime performance cost worse than a GC write barrier (because it incurs writes on reads).
- duneroadrunner 10y ago> Create a vector. Push an element onto it. Take a reference to that element with operator[]. Clear the vector. Call a method on that dangling reference. > Create an object on the stack. Return a reference to that object. Call a method on that reference. References are one of the unsafe C++ elements that SaferCPlusPlus is intended to be used to replace [1]. > Create a vector. Push an element onto it. Call a method on that element that clears the vector and then calls another virtual method on itself, via the this pointer. Yes, that series of operations is safe. A related example from the "msetl_example.cpp" file: typedef mse::mstd::vector<int> vint_type; mse::mstd::vector<vint_type> vvi; { vint_type vi; vi.push_back(5); vvi.push_back(vi); } auto vi_it = vvi[0].begin(); vvi.clear(); try { /* At this point, the vint_type object is cleared from vvi, but it has not been deallocated/destructed yet because it "knows" that there is an iterator, namely vi_it, that is still referencing it. At the moment, std::shared_ptrs are being used to achieve this. */ auto value = (*vi_it); /* So this is actually ok. vi_it still points to a valid item. */ assert(5 == value); vint_type vi2; vi_it = vi2.begin(); /* The vint_type object that vi_it was originally pointing to is now deallocated/destructed, because vi_it no longer references it. */ } catch (...) { /* At present, no exception will be thrown. We're still debating whether it'd be better to throw an exception though. */ } I agree with the gist though. This kind of thing should be prevented at compile time. Rust has an excellent static analyzer/enforcer built into its compiler. Arguably, it would be a service to the community to unbundle it from the Rust compiler and make it available for application to C++ code as well. Arguably. > Accidentally share a vector between threads. Race push_back() and remove(). SaferCPlusPlus addresses the sharing of objects between asynchronous threads [2]. A particular shortcoming of C++ wrt to object sharing is that it doesn't have a notion of "deep const/immutability". > Additionally, the pointer registration mechanism that that library uses has a runtime performance cost worse than a GC write barrier (because it incurs writes on reads). Um, yeah, modern code should try to avoid the use of general pointers (and generally does). Most modern languages don't provide general pointers. SaferCPlusPlus makes them safe and slow (and available for easy porting of legacy code). When writing new code you would instead, when required, use one of the faster pointer types available in the library. Don't interpret SaferCPlusPlus as an assertion that C++ is a uniformly better language than Rust or other modern languages. It's more of a suggestion that C++ and existing C++ code bases can be salvaged to a greater degree than one might think. [1] http://www.codeproject.com/Articles/1093894/How-To-Safely-Pass-Parameters-By-Reference-in-Cplu http://www.codeproject.com/Articles/1093894/How-To-Safely-Pa... [2] http://www.codeproject.com/Articles/1106491/Sharing-Objects-Between-Threads-in-Cplusplus-the-S http://www.codeproject.com/Articles/1106491/Sharing-Objects-...