10 ms·
> It's not memory safe Modern C++ has made big inroads into removing memory safety issues though. Memory safety bugs are relatively uncommon in modern C++ comp
by zik 4y ago
> It's not memory safe
Modern C++ has made big inroads into removing memory safety issues though. Memory safety bugs are relatively uncommon in modern C++ compared to pre C++11 times.
You could argue that C++ is still "unsafe by default" and you'd be right. But most C++ programmers use smart pointers and unique pointers rather than raw pointers now, vectors instead of raw arrays, etc.. And it does make a big difference.
- TazeTSchnitzel 4y agoAren't vectors just as vulnerable to out-of-bounds access as any C-like array? Most C++ additions have plenty of undefined behaviour.
- patrick451 4y agoNot if you stick with the ::at() method, which does bounds checking https://en.cppreference.com/w/cpp/container/vector/at https://en.cppreference.com/w/cpp/container/vector/at
- agwa 4y agoUnfortunately, std::span doesn't have at(). Note that std::vector's at() predates C++11, whereas std::span was added in C++20. It's a myth that C++ is getting more memory-safe; in many aspects it's actually regressing.
- kazinator 4y agoRegardless of all that, you can make your own vector class which works exactly how you want and is completely safe. In a greenfield project where you control every line of C++ code, you can easily achieve very good safety using nothing but ancient C++ features. Not just memory safety. I mean, you can make your own numeric types that do overflow checks and throw exceptions or whatever. It probably won't be very fast, but it will be solid. People have used C++ (ancient C++) to make numeric types with units, where you can't add "kilograms per second" to "meters". I remember that from some talk thing I went to in 1999.
- LaLaLand122 4y agostd::span doesn't have at() because std::logic_error was a mistake. Using std::vector's at() is a mistake (you could use it as a helper method and make it as if it throwed std::runtime_error, but it doesn't, that wasn't its intended usage). All three major C++ standard library implementations have an option to enable assertion checks in both std::vector and std::span operator[]. Enable them unless you are in such a resource limited environment that you can't afford the checks (unlikely). The only reason the standard doesn't require the checks is to allow C++ to be usable in those resource limited environments. You could argue the checks should be enabled by default, but that's not something for the standard to decide, complain to your standard library vendor.
- int_19h 4y agoIt's not just the indexing you have to worry about, though. It's also out-of-bounds iterators. And while, yes, you can tell your implementation to emit checks for those as well, it's so slow in practice that nobody uses it in optimized release builds (and some implementations don't even support such use).
- LaLaLand122 4y agoSure. I agree. I was just arguing that it's good span doesn't have at(), I was not trying to say C++ is memory safe.
- hot_gril 4y agoIt's not that people can't deal with pointers. The most common issue in C++ is how many ways to shoot yourself in the foot with common utils. Like, we use a lot of abseil utils at work that return string_views instead of new strings, and if you're directly passing that somewhere else, you easily corrupt your memory. std::string foo = "foo"; const std::string foo2 = absl::StripAsciiWhitespace(absl::AsciiStrToUpper(foo)); // in Python: foo2 = foo.upper().strip() std::cout << foo2; // garbage Yeah, all technically the user's fault, but it gets pretty tiring worrying about this stuff when you're writing a high-level application and the few CPU cycles you save don't really matter. No matter how good you are, you will make mistakes, and the chance of one bug corrupting memory in your whole process increases exponentially with the LoC written. Also I have to point out the irony in a language being type-safe but not memory-safe. Also, I didn't expect Java-looking class syntax to leave member variables uninitialized (garbage) if you don't set a default. That should at least be a compiler warning, I mean how often do you actually want that?
- okamiueru 4y ago> Also I have to point out the irony in a language being type-safe but not memory-safe. Misnomer aside, there is no contradiction. Type safety is much easier to reason about at compile time than memory management. So, it shouldn't come as a surprise. You need a rust like "borrow checker"-by-default functionality.
- hot_gril 4y agoIt's not surprising that they use static types in C++, but it's not about safety. It's about knowing the size of everything at compile time. Dynamic typing requires moving more towards the "interpreted" side of languages and incurring the performance cost of that. The contradiction is when the veteran SWEs I work with say we use C++ instead of Python for the added safety of compile-time type checks, treating dynamic typing like a bigger risk than memory trampling (never mind that Python has type-checking too if you really want it).
- bluGill 4y ago
- moefh 4y ago> Modern C++ has made big inroads into removing memory safety issues though Modern C++ adds new features that allows you to write code that doesn't have as many memory safety problems, but all the unsafety (is that a word?) that existed in the past still exists. Removing features that allows memory unsafety will probably never happen, because one of the main reasons people use C++ is all the legacy code will still be used for decades. It's great that you can write C++ using only the newest features, and that does increase safety. But that doesn't offer the same guarantees of languages designed to simply not have these kinds of problems, or that by design flag the points in the code where safety can't be guaranteed by the compiler.
- kazinator 4y agoYou can write completely memory-safe code using nothing but C++98 features, like templated smart pointers and whatnot, which wrap around the unsafe parts of your code. You can keep that unsafe part small and get it right "by inspection"; then if it's used through the higher level primitives, it cannot be misused.
- moefh 4y agoThe problem is that there's no way to automatically check any of that. If the code is small and doesn't change or doesn't have to last for too long, that's OK. But when it's changed over the years, it's pretty much impossible to guarantee that the unsafe parts are kept small and contained, especially as programmers leave and new ones come in.
- midoridensha 4y agoC++ has so many features that it looks like a different language, depending on who's writing it. It can still compile decades-old code written to the oldest standard, or brand-new code with the latest memory safety features. So is there a compiler flag or external linting tool that can generate warning messages when you're using an older memory-unsafe technique where a safer and more modern technique could replace it?
- Kranar 4y agoThis is simply false, modern C++ just replaces old issues with new ones. There are numerous new unsafe footguns that "modern" C++ introduced with lambda captures, coroutine lifetimes, and libraries like std::range where lifetimes and ownership are incredibly difficult to reason about. Not to mention that the web is full of blog posts about how to properly use std::string_view, since a lot of crashes trace back to improper use of it, particularly its constructor that kind of acts like it takes ownership but doesn't.
- lmm 4y ago> Modern C++ has made big inroads into removing memory safety issues though. Memory safety bugs are relatively uncommon in modern C++ compared to pre C++11 times. I remember when people said the same thing about C++03. Plus ca change.
- kazinator 4y agoIt was true, which makes it silly to be saying it about C++11, C++14, C++17, ... In C++98 you could develop a program with some unsafe core of whatever resource management you wanted, and then wrap it with the right classes so that it can't be misused. After circa 1995, anyone struggling memory safety issues in greenfield C++ code that was completely under their control (no legacy) had to be a supreme goofball not to be leveraging the language to make that sort of thing go away.
- lmm 4y agoAnd yet we keep seeing memory safety issues in C++ code even when that code was written greenfield long after 1995. (E.g. IIRC cloudflare's cloudbleed was in a codebase that had been started in about 2012).
- kazinator 4y agoLooking at this, that is not clear. The report below (courtesy of Wayback Machine) mentions some external HTML libraries, as well as the development of modules that plug into NGINX, and work on raw memory buffers (I'm guessing, dictated by NGINX). Hard to speculate without seeing the code. Not that it's an excuse; greenfield C++ code could find ways to interface in a bullet-proof way with something or anything that communicates using low-level buffers. The "Ragel" tool they used evidently generates C that uses raw pointers: > The Ragel code is converted into generated C code which is then compiled. The C code uses, in the classic C manner, pointers to the HTML document being parsed, and Ragel itself gives the user a lot of control of the movement of those pointers. The underlying bug occurs because of a pointer error. The bug in these Ragel-generated parsers was somehow hidden or compensated for by some buffering strategy, which they tweaked when introducing some new kinds of parsers "cf-html". Those didn't have the bug, but the different buffering turned on for them exposed the bugs in the Ragel based parsing. https://web.archive.org/web/20170223233000/https://blog.cloudflare.com/incident-report-on-memory-leak-caused-by-cloudflare-parser-bug/ https://web.archive.org/web/20170223233000/https://blog.clou... I'm looking at the Ragel State Machine Compiler user guide. Chapter 5 (Interface to Host Program) makes it quite clear what sort of thing the Cloudfare people chose to grapple with. Ragel will write code for you planted into middle of any C function anywhere; you must provide numerous predefined variables, under prescribed names, some of which are pointers to data, and so it goes. For some languages, there are safer interfaces: for Java and Ruby there is a buffer and instead of a pointer there is an index into it. Ragel could have been upgraded to have actual C++ support of some kind.
- pjmlp 4y agoMost programmers sadly keep using C idioms even while writting "modern" C++, just look at recent samples provided by companies with seat at the ISO table, some of which have even provided famous security reports.