13 ms·
Circle C++ with memory safety
- KerrAvon 2y agoHaven’t read through it in enough detail yet to fully understand the language changes, but the authors are absolutely correct on some basic background that other folks don’t always understand: > Memory-safe languages are predicated on a basic observation of human nature: people would rather try something, and only then ask for help if it doesn't work. For programming, this means developers try to use a library, and only then read the docs if they can't get it to work. This has proven very dangerous, since appearing to work is not the same as working. 100% 100% 100%
- dataflow 2y agoIt's an elegant sentence but it's incorrect to say memory save languages are predicated on that? Even a room full of C++ experts who understand this completely and write their code strictly based on formal contracts will still eventually write memory bugs. Memory safe languages are just predicated on the memory-safety problem being difficult to avoid for humans, because nobody has a 0% error rate. They would still be incredibly necessary and relevant even if nobody relied on "appears to work" as the measure of correctness.
- pornel 2y agoI think the point is that Rust encodes more rules in its interfaces (ownership, lifetimes, thread safety). If you misunderstand how a Rust library works, your code most likely won't compile instead of silently causing UB. The rules for safe interfaces are the same for all Rust programs, so users know what to expect. Whereas in C++ the library author has more to say what usage is supported and what isn't (e.g. Rust requires all globals to be thread-safe unconditionally, but a C++ library may say it's thread safe if you set a config option or it's your problem to synchronize access).
- tialaramex 2y agoYou already wrote "100%" enough times, so I'll add that Rust's technology, and Rust's culture, still aren't enough, you have to really put the work in counteract this very powerful danger. Rust's technology + culture should mean you won't blow your foot off with this (entirely human) approach in their language, but you can definitely give yourself a nasty splinter, destroy your customer's data, and a million other dumb things. For example Rust provides types like core::cmp::Ordering, core::time::Duration and core::ops::ControlFlow so sometimes your API will be harder to misuse than in might have been because you know, your timeout parameter was a Duration, not an integer count of seconds (or was it milliseconds?) But, although eventually Clippy will express doubts, Rust won't force you to rewrite that function which took N booleans and now after modification takes N+1 booleans, even though all your callers are probably a mess of true, false, true, true, false undecipherable flag nonsense and a re-factor was called for. It's surprisingly hard to give new programmers the right instincts on this stuff. I'm pretty sure I was terrible (thirty years ago) too, so this isn't a humble brag it's just an observation.
- maxloh 2y agoAlso, check out Google's in-development Carbon Language, designed to address the same issues with a Kotlin-like approach. It's an entirely new language that could interoperate with existing C++ code/library. https://github.com/carbon-language/carbon-lang https://github.com/carbon-language/carbon-lang
- jjnoakes 2y agoDoes Carbon have any undefined behavior? Some of the wording in their goals document suggests that they do/will, but it isn't clear to me.
- gumby 2y agoUnless you solve the halting problem, every language has undefined behavior.
- tialaramex 2y agoNope. A language which has semantic requirements and isn't willing to reject programs for which it has been unable to determine whether they meet the requirements, will have these cases by Rice's Theorem. This is why C++ is a complete disaster. C++ specifically does not allow the compiler to reject such programs. You can defuse this like (safe) Rust by just writing conservative checking. You will reject some programs you would like to have successfully compiled, but that's actually OK, programmers who hate it are inspired to improve the checking, that's what Rust's "Non-Lexical Lifetimes" - mentioned by Sean, are about. Even more radically, you can be a Special Purpose language and just only allow a relatively small subset of possible programs, all of which you know are correct, that's why WUFFS gets to emit indexing with no runtime bounds miss checks - it did all the bounds checks at compile time, all source where that wouldn't be possible isn't legal WUFFS code.
- mgaunard 2y agoThat is incorrect. A C++ compiler is perfectly allowed to reject any program that contains undefined behaviour. It's just not required to do so.
- alebfv 2y ago[dead]
- tialaramex 2y agoSean (the author of Circle) is an impressive guy. He started pursuing this work at about the same point several of the "C++ Successor Languages" were created, but although all of them claimed to be about solving this problem, especially when first announced, they actually don't have a solution unlike this Circle work. Let me briefly enumerate: Val (now HyLo) says it wants to solve this problem... but it doesn't yet have the C++ interop stuff, so, it's just really a completely different language nobody uses. Carbon wants to ship a finished working Carbon language, then bolt on safety (somehow) but, only for some things, not data races for example, so, you still don't actually have Rust's Memory Safety Cpp2 explicitly says it isn't even interested in solving the problem, Herb says if he can produced measurements saying it's "safer" somehow that's good enough. It's interesting how many good ideas from Rust just come along free for the ride when you try to do this, do you like Destructive Move? Good news, that's how you make this work in Rust so that's how Circle does it. Exhaustive Pattern Matching? Again, Circles does that for the same reason Rust needs it. It is certainly true that this is not the Only Possible Way to get Safety. It would be profoundly weird if Rust had stumbled onto such a thing, but "Let's just copy the thing that worked in Rust" is somehow at once the simplest possible plan that could work and an astonishing achievement for one man.
- Vt71fcAqt7 2y ago>Carbon wants to ship a finished working Carbon language, then bolt on safety (somehow) but, only for some things, not data races for example, so, you still don't actually have Rust's Memory Safety I'm not sure this is correct. As I understand it, Carbon's plan is to add a borrow checker like Rust's. From a recent talk[0][1] by one of the lead developers: >Best candidate for C++ is likely similar to Rust’s borrow checker [0] slides: https://chandlerc.blog/slides/2023-cppnow-carbon-strategy/index.html#/105 https://chandlerc.blog/slides/2023-cppnow-carbon-strategy/in... [1] relevant timestamps: https://youtube.com/watch?v=1ZTJ9omXOQ0&t=1h31m34s https://youtube.com/watch?v=1ZTJ9omXOQ0&t=1h31m34s https://youtube.com/watch?v=1ZTJ9omXOQ0&t=1h9m49s https://youtube.com/watch?v=1ZTJ9omXOQ0&t=1h9m49s
- tialaramex 2y agoChandler has explicitly said that he doesn't see a reason to solve data races. The borrow checker isn't enough on its own to solve this in Rust, Sean explains (probably deeper in Circle's documentation) that you need to track a new property of types which is infectious, Rust does this with the Send and Sync traits.
- reynmorris 2y agoA lot of coders already do this. My STL replacement has Vector and VectorUnsafe. Vector is checked to the hilt for bounds safety, stack safety, UB safety, etc. and is slower. But if I have a tight loop, I can use VectorUnsafe and just make sure I'm being careful, and it has no checks at all.
- jjnoakes 2y agoCare to share your Vector implementation that is "stack safe" and "UB safe"?
- reynmorris 2y agoIt's simple, whether the backing memory is heap or stack, it's bounds checked. And overriding all the operators and only returning safe types prevents many types of undefined behavior. Of course you can fuck with it enough to make it unsafe, but at that point you know exactly what you're doing
- tialaramex 2y agoWithout seeing it, of course it's hard to write examples, but typically for this type of thing it turns out that "fucking with it" enough to be unsafe is easy to do by mistake and so you end up basically saying resorting to classic C++ "Nobody will make mistakes" safety which we know doesn't work. In some cases you can even "fuck with it" less than std::vector and cause memory unsafety because std::vector was implemented by people who've been fucked with before, and this "safe" collection maybe was not. Pushing items from the collection itself into the collection again when it's full is often one way to cause this - the std::vector promises this works correctly.
- reynmorris 2y agoYou can assume that the collection doesn't have good coverage, but what I'm saying is the constructs in the C++ language are there to make it have good coverage. Pair this with some Clang sanitation (like banning raw pointers) and you'd have to go out of your way to make it unsafe.
- rurban 2y ago> rust: rigorous memory safety > circle : unsafe printf Comeon people, if you allow unsafety, you cannot call your language safe. There are safe system languages, but don't lie and call unsafe languages safe. Partial safety is not full safety.
- wild_pointer 2y ago> circle: rigorous memory safety > rust: unsafe { println!("{}", *r1); } Comeon people, if you allow unsafety, you cannot call your language safe. There are safe system languages, but don't lie and call unsafe languages safe. Partial safety is not full safety.
- lpapez 2y agoUnsafe in Rust is not unsafe in the same sense that C/C++ UB is unsafe. Unsafe in Rust means "soundness cannot be statically verified, the language will insert runtime checks for you and perform a clearly defined action (panic) if they are violated". Much ink has been spilled about "unsafe" in Rust being unfortunately named.
- j16sdiz 2y agoNo. No. And no. > It is the programmer's responsibility when writing unsafe code to ensure that any safe code interacting with the unsafe code cannot trigger these behaviors. https://doc.rust-lang.org/reference/behavior-considered-undefined.html https://doc.rust-lang.org/reference/behavior-considered-unde... And they transverse > However, violations of these constraints generally will just transitively lead to one of the above problems. https://doc.rust-lang.org/nomicon/what-unsafe-does.html https://doc.rust-lang.org/nomicon/what-unsafe-does.html
- loeg 2y ago> Unsafe in Rust is not unsafe in the same sense that C/C++ UB is unsafe. Unsafe in Rust means "soundness cannot be statically verified Right. But as sibling points out, the rest of your sentence is incorrect. The language mostly does not insert additional, runtime checks and you are allowed to create UB-level bad behavior in unsafe blocks.
- geertj 2y agoFirst came across this 2 days ago and found this extremely impressive. There's also a YouTube presentation where Sean goes over the main features of Circle's C++ memory: https://www.youtube.com/watch?v=5Q1awoAwBgQ https://www.youtube.com/watch?v=5Q1awoAwBgQ. This seems to be adding Rust borrow semantics and borrow checking to C++. The delivery vehicle seems to be a C++ compiler that the author has been working on for a number of of years. I couldn't find a ton more on the background of this. From a technical perspective this looks promising as a C++ successor language. The project will have to attract other members in the C++ community though.
- secondcoming 2y agoHe’s active on the C++ Slack, regularly asking questions about the minutiae of the C++ spec. It seems like a massive headache.
- mgaunard 2y agoMemory-safe languages are IMHO a distraction from making people write bug-free code. There are much more important bugs than memory safety bugs, including all sorts of performance bugs. We need to address the whole spectrum instead of compromising all areas in order to tackle niche bugs that only matter in security-critical contexts.
- School-Cotton 2y agoIt’s not true that memory safety bugs only matter in security-critical contexts. If a program segfaults, it’s going to make the user unhappy regardless of security implications.
- Yoric 2y agoAnd don't forget that a segfault is the lucky case in matters of memory safety bugs. I've worked on many memory safety bugs that manifested themselves by silently corrupting data.
- kiitos 2y agoMemory safety bugs are second only to segfaults in terms of importance.
- logicchains 2y agoIn terms of the actual bugs encountered when developing modern C++ applications, logic bugs vastly outnumber segfaults and memory safety bugs, which are quite rare when you're writing business logic.
- bluGill 2y agoI fully agree. Memory safety issues are a small minority of C++ issues. Memory safety issues when you get them are very difficult to track down, and often have much broader/worse effects.
- erik_seaberg 2y ago
- Guvante 2y agoThis sounds a lot like C++/CLI which has Microsoft backing and has a whole .NET sub language. The problem was even when written together the impedance mismatch existed.
- logicchains 2y agoIt's quite ironic that on a page about safety they use an if statement without {} braces: if(x % 2) vec^.push_back(x); unsafe printf("%d\n", x); Skipping the braces for single line conditionals in C++ is a lazy practice that almost inevitably leads to bugs, like Apple's famous goto fail bug: https://www.synopsys.com/blogs/software-security/understanding-apple-goto-fail-vulnerability-2.html https://www.synopsys.com/blogs/software-security/understandi... . While memory bugs are difficult to prevent, this particular class of logic bug can be eliminated entirely just by always remembering to write two {} around the statement, so a language concerned with correctness should promote such good practices. if(x % 2){ vec^.push_back(x); }
- jandrewrogers 2y agoThis is nitpicking style, there are no real safety implications. Compilers will happily inform you if the parse is ambiguous or code is unreachable.
- logicchains 2y agoIf you write in this style then there's the possibility that someone else (or yourself in future) will add a second line, like: if(x % 2) vec^.push_back(x); do_something_else(); under the mistaken assumption that the second line will also only run in the x % 2 case. People make mistakes, this particular one can happen, does happen, and has happened many times, and the compiler absolutely will not inform or warn you, because it doesn't know you intended the second line to run in the same if statement as the first (because C++ isn't whitespace sensitive). Is ruling out an entire class of bug really not worth the effort of just typing two more characters?
- j16sdiz 2y agoIndentation can be fixed with a code formatter in pre commit hook.
- 2y ago
- jandrewrogers 2y agoMany of the criticisms are of the C++ standard library design and implementation rather than C++ the language per se, particularly with respect to undefined behavior. Much of this, in turn, is because the C++ standard library is very old and anything new added to it must be compatible and interoperable with all of the older parts, whether or not it is a good idea. Borrow checking is a separate matter. Modern C++ provides all the tools to build an alternative standard library from the ground up with most behaviors and interactions being defined and safe. This always seemed like lower-hanging fruit for a first attempt than changing the language. C++ is commonly used in contexts where object lifetimes and ownership are inherently unknowable at compile-time, safety can only be determined at runtime. This is why memory safety annotations like 'std::launder' exist in C++. Trying force these cases into compile-time memory safety box is a source of much friction when using languages like Rust. They handle the 80% case where compile-time safety, destructive moves, etc make things easy for the developer but then significantly worsen the complexity, and therefore safety, of the other 20%. This isn't necessarily a bad thing but it intrinsically creates a market for C++ which explicitly allows you to handle these cases in a reasonable way. Systems programming is full of edge cases that break tidy programming language models. This has to be accommodated reasonably in languages that want to wear the mantle of "systems language". Zig is an example of a new systems language that does this well in my opinion.
- pjmlp 2y agoZig is basically Modula-2 with a revamped syntax for C folks, plus compile time metaprogramming. And yes, many of these issues were already solved by other system programming languages outside the UNIX linage of programming languages.
- 112233 2y ago> Modern C++ provides all the tools to build an alternative standard library from the ground up with most behaviors and interactions being defined and safe. It also goes to a great lenght to embed standard library into base language as deeply as it can, and forces unsafe constructs. Just see how many magic types from std namespace are emmited by compiler when you use range for, initializers, or, worst of all, coroutines. Lambdas with capture by reference replace old fashioned dangling pointers with much more modern dangling references (you are free to copy lambdas that reference local variables :smiling virus with thumbs-up emoji:). There is no way to get "another library", without making it not-c++. Those guys cannot even make no-exceptions/no-rtti a part of the standard.
- legobmw99 2y agoI think a challenge to this evolution path is the same as what motivated Sutter’s cpp2: defaults I think the premise that a lot of experienced programmers can write good C++ is at least somewhat valid. They know to not use raw pointers, which APIs to use for bound checking, whatever. The issue is that new users don’t, and the defaults are bad. If I have to write #feature on safety in every file, it becomes possible to forget. Opting in vs opting out
- ljlolel 2y agoWho is using Circle C++ in production?
- grumpyprole 2y agoThere was once a time when nobody used C++ in production.
- ljlolel 2y agoSorry are you saying nobody is using Circle? Also it seems closed source?
- quietbritishjim 2y ago> There's only one systems-level/non-garbage collected language that provides rigorous memory safety. That's the Rust language. Honest question: what about Ada? It was specifically designed to be a safe language for critical systems, and for a while was mandated for some military systems. Did the author not consider it, or are its protections just not as expansive as Rust's?
- steveklabnik 2y agoI’m not the author, but there’s a few reasons Ada tends to be forgotten in these discussions: Back when Ada was new, people just didn’t actually like programming in it much. Some did, of course, but many did not. This is the reason the Ada Mandate was abandoned. This led into a situation with a small, walled off community that didn’t really communicate with the outside world much. This has a compounding effect over time. Ada, while designed for safety critical systems, was not actually memory safe until fairly recently. Deallocating memory at runtime wasn’t, and in my understanding, may only be in the presence of SPARK? Hopefully someone can chime in here. Now, that fine for the systems Ada tended to be used for, which often have either no dynamic allocation or a singular allocation at program startup, but for inspiration for other language designs, given that it forgoes a hard problem, it’s not really as useful to those who are trying to solve those problems. This doesn’t mean Ada is useless for inspiration, but for “how do I implement memory safety,” it doesn’t have many unique uses things to offer. None of this means Ada is a bad language, but these are the main contributing factors that I see with regards to your specific question.
- pjmlp 2y agoThat is the usual Ada outdated image, just like unsafe code blocks in Rust, those Unchecked Deallocation are wrapped in safe calls, and since Ada95 there are controlled types, allowing for RAII patterns. Additionally, it is common to use arenas, and many types can be dynamically stack allocated with a retry operation in case there is not enough space, so that the call can be redone with a smaller size. Memory Management with Ada 2012 from FOSDEM 2016, https://archive.fosdem.org/2016/schedule/event/ada_memory/ https://archive.fosdem.org/2016/schedule/event/ada_memory/ Doesn't go much into SPARK related improvements though, given its date.
- pizlonator 2y agoNice to see Sean cite Fil-C, though he does it much more in passing than it deserves, considering that Fil-C gives you memory-safe C and C++ without requiring any annotations whatsoever. He cites it as a sanitizer and references undefined behavior, which isn't really how I would describe it (unlike a sanitizer, it catches all memory safety bugs, and also unlike a sanitizer, it turns a lot of what would have been UB in C++ into defined-but-safe behavior). It's a very different approach from Sean's. For example, Circle implies broad overhaul to tooling and libraries while Fil-C implies no such thing. Also, Circle is all about not using existing compilers on the grounds that they are hard to experiment with, while Fil-C is a surgical change to clang/LLVM. The main advantage of Circle over Fil-C is that if you pay the cost of that overhaul, you'll get performance that is better than what Fil-C could ever do even with a lot of optimization. Not obvious to me if that makes it net better, though.
- tialaramex 2y agoFor the performance, there are a bunch of people, some of them probably wrong and others definitely right, who believe they need the best possible performance from software. You can sell these people something like Rust because you can very often either show why the "better performance" C++ they have is wrong (and if they wanted a wrong answer here's zero already, pay me) or sometimes actually worse performance. Not every time, but often enough to make a real difference. The Circle safety feature should be in the same ballpark. You can't sell them anything that's just anyway going to have worse performance, if you could they'd be writing Java already. So that's counting against Fil-C.
- pizlonator 2y agoJava is a totally different language, so it’s not even remotely a competitor in this space. Also Java is quite fast, even compared to C or Rust. Fil-C is all about being able to run existing C/C++ code that nobody is going to rewrite, not even in a dialect like Circle, since the burden of annotations will be too great.
- mike_hearn 2y ago
- deleted 2y ago[deleted]
- Asooka 2y agoI'm not sure that last paragraph had to be so toxic. We've had enough toxic egos in this industry, we don't need any more. I've never heard of this person before, but I do not think I wish to be part of his project.
- roca 2y agoIt's an impressive project. One of the key problems for any safe language interoperating with C++ (even a safe C++ subset) is that you really want to be able to interact with C++ standard library types (string, vector etc) safely because they will appear at interface boundaries. Circle introduces its own safe standard library so I don't see how Circle fares here.
- andrewstuart 2y agoFALSE - this is so misleading - these statements imply that 68% to 70% of ALL SECURITY ISSUES are due to memory safety - that's wrong and those quotes leave out the important context. From the post: Microsoft's bug telemetry reveals that 70% of its vulnerabilities would be stopped by memory-safe programming languages. Google's research has found 68% of 0day exploits are related to memory corruption. These sorts of false statements make people everywhere think that memory safety is a bigger issue than it is and that it impacts almost all application development which is completely wrong.
- binarycrusader 2y agoDisclaimer: Speaking only for myself. memory safety is a bigger issue than it is and that it impacts almost all application development which is completely wrong. It specifically states that in the context of vulnerabilities so I'm not sure why it's believed to be insufficiently qualifying. With that said, while you're right to question the precisions of statements, Google says the same for their own data: https://www.chromium.org/Home/chromium-security/memory-safety/ https://www.chromium.org/Home/chromium-security/memory-safet... Around 70% of our high severity security bugs are memory unsafety problems (that is, mistakes with C/C++ pointers). Half of those are use-after-free bugs. ...so it's not just Microsoft saying that. You'll also see this mentioned on Wikipedia: https://en.wikipedia.org/wiki/Memory_safety https://en.wikipedia.org/wiki/Memory_safety Going farther, the numbers vary, sometimes even more: https://www.memorysafety.org/docs/memory-safety/#how-common-are-memory-safety-vulnerabilities https://www.memorysafety.org/docs/memory-safety/#how-common-... Extremely. A recent study found that 60-70% of vulnerabilities in iOS and macOS are memory safety vulnerabilities. Microsoft estimates that 70% of all vulnerabilities in their products over the last decade have been memory safety issues. Google estimated that 90% of Android vulnerabilities are memory safety issues. An analysis of 0-days that were discovered being exploited in the wild found that more than 80% of the exploited vulnerabilities were memory safety issues. In short, I don't think the context here will change most folks' interpretation of the results--nor do I think it should. Any network-connected device is inherently multi-user and the security of that device is only as effective as the least secure program executing on that device at a particular permission level. I can think of very few applications today that don't interact with a network in some way. In addition to that, my opinion is that most memory-safety issues that create vulnerabilities are also reliability issues. Secure applications are generally inherently more reliable applications.
- madebydouglas 2y agoIt seems like if this can be the next version of C++ that would be a good thing, but if it's just another C++ like language, such as Carbon (even with proposed benefits), aren't we just going in circles here? Wouldn't a better approach be to instead focus on adding better C++ interop into Rust? Isn't that a more forward thinking approach? On that line of thinking, what about Swift and its C++ interop? Or is Swift not ready for systems level programming?
- zer0zzz 2y ago> On that line of thinking, what about Swift and its C++ interop? Or is Swift not ready for systems level programming? A lot of swift behavior is dictated by the standard library. There have been Micro-Swift and embedded swift implementations to cut down overhead along with work on move only types. I think swift could be a good candidate for a post-c++ language but despite the open community it is highly Apple dominated in its scope and direction. That’s probably my the biggest trouble with swift.
- _huayra_ 2y agoSean Baxter was on cppcast recently, highly recommend anyone interested to give it a listen: https://cppcast.com/safe-borrow-checked-cpp/ https://cppcast.com/safe-borrow-checked-cpp/
- throwawayk7h 2y ago> Institutional users of C++ should be worried. The security community is baying for this language's extinction. The government is on our case about it, and legislation banning C++'s use in certain sectors looks like a possibility. I don't think nitpicking the NSA is a compelling response. I am a bit worried about this. should I be?