4 ms·
The table stakes here is automatic bounds checking. This is something that pretty much every newer language does already, and even several older languages figur
by jcranmer 3y ago
The table stakes here is automatic bounds checking. This is something that pretty much every newer language does already, and even several older languages figured out how to do well.
The problem in C/C++ is that pointers don't inherently communicate their bounds, so your options for adding automatic bounds checking are a) fat pointers and consequently (severe) ABI break; b) some sort of shadow memory to store bounds info (ASAN, generally considered inadvisable to use in production); or c) change the language to communicate what the bounds of a pointer are. The good news is that most interfaces will provide the bounds of a pointer as another member of the struct or the function parameter it's part of; the bad news is that actually communicating that information requires a scope lookup change that is hard to get through the committees.
- physicsguy 3y agoThe reason they don't communicate their bounds is also a performance optimisation. You can certainly do it in C++; use a std::vector for e.g. and use the .at() method to index on it and it'll throw an exception unless you disable that with a compiler flag. The thing is, it's fine to take that risk if you're writing HPC simulation software, but it's much less fine if you're writing an operating system or similar.
- pizlonator 3y agoYeah, “also” a performance optimization. It’s also just legacy. We’ve always done it that way so we still do it that way for ABI compat and because it’s hard to find a compiler that does it any other way. Imagine if the story was: “you totally can have a bounds on your ptrs if you pass a compiler flag and accept perf cost”. I bet some of us would find that useful.
- chlorion 3y agoThe performance and power use cost to checking bounds is trivial! Apple has tested this, on mobile devices even, when working on -fbounds-safety. From the slides: System-level performance impact • Measurement on iOS • 0-8% binary size increase per project • No measurable performance or power impact on boot, app launch • Minor overall performance impact on audio decoding/encoding (1%) • System-level performance cost is remarkably low and worth paying for the security benefit Some more specific synthetic benchmarks suites reported ~5% runtime cost for bounds checking. https://www.youtube.com/watch?v=RK9bfrsMdAM https://www.youtube.com/watch?v=RK9bfrsMdAM https://llvm.org/devmtg/2023-05/slides/TechnicalTalks-May11/01-Na-fbounds-safety.pdf https://llvm.org/devmtg/2023-05/slides/TechnicalTalks-May11/... Bounds checking being omitted due to performance is mostly a myth, the only time this should ever be believed is in very specific circumstances such as performance critical code and when the impact has actually been measured!
- physicsguy 3y agoWhether it's trivial or not depends totally on the workflow. A 5% runtime cost can be enormous - when I was in academia I was running thousands of simulations on big clusters like ARCHER, some of which could take up to a fortnight to run. In those cases, a 5% cost can add a whole other working day to the runtime!
- deathanatos 3y ago> Whether it's trivial or not depends totally on the workflow. People here are talking about language defaults, and that the default should be safe, and while, yes, technically you can construe a workflow they're not going to work for, they work for most. That doesn't prevent your ARCHER simulation from calling — hopefully only at sites that profiling indicates need it — .yolo_at(legit_index_totes) (or whatever one might call the method) & segfaulting after burning a few days worth of CPU time away.
- paulddraper 3y agoDo you believe that is a common case, or an exceptional one?
- physicsguy 3y agoI don't think it's particularly exceptional for the sorts of people that are still using C++ (and making a conscious decision to do so over Rust for e.g.). If you're writing 'standard' C++ these days, you're probably already making use of std::array, std::vector, etc. anyway. The only area where people are working on modern codebases I've not seen so much of that is in HPC stuff and embedded.
- JohnFen 3y ago> You can certainly do it in C++ You can do it in C as well, although it's a lot clunkier. I've been doing so for decades when the effort is appropriate to the task.
- pizlonator 3y agoThings like CHERI, Fil-C, and CCured make pointers just carry their bounds. It’s not an unfixable problem. I wish we were talking about fixing it, not making excuses.
- lenerdenator 3y agoThe problem with fixes on things this low-level is that they carry the potential to break lots of code. Since broken code has to be fixed, you then get into the "why not just rewrite it in <insert new hotness here>?" argument, which is headed off by just not fixing it. C/C++ maintainers knew this and didn't want to see their lives' work made less significant. Now the issue's been forced by (among other things) one of the world's most influential software customers, the US Federal Government, implying that contract tenders for software written in languages like Rust will have an advantage over those written in languages that don't take memory safety as seriously.
- pizlonator 3y agoCHERI claims that the amount of changes are exceedingly small. Fil-C is getting there. So, C has a path to survival. > The problem with fixes on things this low-level is that they carry the potential to break lots of code. Since broken code has to be fixed, you then get into the "why not just rewrite it in <insert new hotness here>?" argument, which is headed off by just not fixing it. “Lots” is maybe an overstatement. Also, if there was a way to make C++ code safe with a smaller amount of changes than rewriting in a different language then that would be amazing. The main shortcoming of CHERI is that it requires new HW. But maybe that HW will now become more widely demanded and so more available. The main shortcoming of Fil-C is that it’s a personal spare time project I started on Thanksgiving of last year so yeah
- marcosdumay 3y ago> CHERI claims that the amount of changes are exceedingly small. Oh, man. Yes, they do. Many people have been claiming that for decades. When can we expect one of them to claim it's done? (To be fair, the amount of changes required has been diminishing through those decades.)
- uecker 3y agoThe problem is existing practice. GCC has solved this problem for function parameters a long time ago with parameter forward declarations. But other compilers did not copy this GNU extension, and also nothing else really emerged... This makes it hard to convince the committee to adopt it. In structs there is no existing extension, but a simple accessor macro that casts to a VLA type works already quite well and this can be used by refactoring existing code. There are still some holes in UBSan, but otherwise I think you can write spatially memory-safe C more or less today without problem. The bigger issue is temporal safety, so the bigger puzzle piece still missing is a pointer ownership model as enforced by the borrow checker in Rust.
- jcranmer 3y ago> There are still some holes in UBSan, but otherwise I think you can write spatially memory-safe C more or less today without problem. I wouldn't call it a solved problem until gcc and clang have an auto-inserts-bound-check flag that does the equivalent of a Rust panic on every array access if it's out-of-bounds, is considered usable on production code [1], and works on most major projects (that care enough to change their source to take advantage of this flag). Overall, the problem isn't so much that we don't know how to write safe C code, it's that the compiler doesn't quite have enough information to catch silly programmer mistakes, and there current situation is juuuuust bad enough that we can't feasibly make code that doesn't tell the compiler enough error out during compilation. > The bigger issue is temporal safety, so the bigger puzzle piece still missing is a pointer ownership model as enforced by the borrow checker in Rust. Temporal safety is interesting in part because it's not clear to me that there currently exists a good solution here. The main problem, like existing partial solutions for spatial memory safety, is that the patterns to make it work well are known, but programmers tend to struggle to apply all of the rules correctly. Rust's borrow checker is definitely a step up from C/C++, but at the same time, there are several ownership models that it struggles to be able to express correctly, even if you ignore the many-readers-xor-one-writer rule that it also imposes. Classic examples are linked lists or self-referential structs, but even something like Windows' IOCP can trip up Rust's lifetime system. Although, at the very least, a way to distinguish between "I'm only going to use this pointer until the end of the function call" and "I'm going to be responsible for freeing this pointer you give me, please don't use it any more" would be welcome to have, even if it is a very partial solution. [1] Don't get me wrong: the development of the sanitizers is an important and useful tool for C/C++, and I strongly encourage their use in test environments to catch issues. It's just that they don't meet the bar to consider the issue solved.
- rerdavies 3y agoThe problem is fixable in C++. std::span is the fat pointer; std::array is the checked array. All that's missing is a compiler option that gives warnings/errors when the legacy native [] features are used. C is probably unfixable. But that's a different language. Presumably compilers would allow conversion of spans to native pointer arguments when calling methods declared as "extern 'C'".
- pjmlp 3y agoVisual Studio does exactly that, yet most devs don't care until the goverment steps in.