7 ms·
What science can tell us about C and C++'s security (2020)
- dcow 5y agoI like this style of discourse. Here's some data, here's how you could prove me wrong, let's talk when you discover additional data. It's too easy to say something like, "well I see your data but since companies care about features not bugs and we can't rewrite everything in safe languages ... and I know 3 people who can write safe C ...". A statement like this does not disprove the author and in a way actually detracts from the discussion. Normally you'd need a moderator to keep people on the rails. It's neat to see the person arguing lead with their impression of what could further the discussion and invite others to participate logically.
- pjmlp 5y agoLets also not forget that AT&T tried to fix C with Cyclone, and lint was already available in 1979, so they definitely knew what child they have placed into the world.
- deleted 5y ago[deleted]
- gizmo 5y ago> In conclusion, the empirical research supports the proposition that using memory-safe programming languages for these projects would result in a game-changing reduction in total number of vulnerabilities. I think this is too strong a conclusion. How many of the major data leaks and ransomware attacks exploit memory safety issues? Not many. The bulk of them target misconfigurations, vulnerabilities caused by bad text-based protocols, logic errors in software, and social engineering. That's not to say memory safety doesn't matter, and you can get very pernicious and subtle bugs when you get too clever in C-based languages. That said, the languages that boast about their memory safety are written in C/C++ (python, ruby, Java, llvm) and run on operating systems that provide process isolation with memory safety written in C/C++ on top of hypervisors which are also written in C/C++. You can argue, as the article does, that use of C/C++ inevitably results in many memory safety issues, and that therefore we should use memory safe languages. Except this doesn't take into account the entire categories of vulnerabilities that have been entirely eliminated because of good C/C++ abstractions like process isolation, virtual memory, filesystems, tcp-ip, hypervisors, and so on. But we take these luxuries for granted, and the benefits they confer become invisible. I think there is a much more mundane lesson here. Good abstractions prevent entire classes of vulnerabilities, and bad abstractions are leaky no matter how careful you are. C and C++ are pretty bad languages insofar they give you limited options for building good abstractions, but with careful programming it can be done and much of the best and most reliable and most complex software is written these low level memory unsafe languages and all major security advancements we've actually made in the real world are still implemented in memory unsafe languages.
- jstimpfle 5y ago> I think there is a much more mundane lesson here. Good abstractions prevent entire classes of vulnerabilities This is how I try to write C, in the end there there is very little "unsafe" code in it - few complicated pointer dereferences, few dynamic allocations, almost no transfer of ownership anywhere. No callbacks, and requiring e.g. a ref-counted handle is very rare. Mostly function calls, pointers only valid during function call (or make a copy, e.g. ID strings), creating the data in the module that understands it, using suitable allocation strategies and centralized resource release. Asynchronous messaging instead of hard to follow callbacks (which break the flow of execution). The current code I have has grown to be devoid of dynamic memory allocations simply for performance/latency reasons. It now has good MISRA compliance without ever trying hard. There have been a few bugs (including very silly and obvious concurrency bugs) but due to the app being so "fixed" those have always turned up pretty quickly (so far) and have been easy to spot and fix. For larger projects, I feel like the value of bottled abstractions is more and more decreasing. To connect things in an efficient and maintainable way all these layers have to be unwrapped.
- dcow 5y agoThe Rust compiler essentially automates all this. Nobody is saying you can't write good C. The argument is that the data suggests a memory-safe language eliminates an entire realm of possibilities available in C. This is an attempt to push the general impression out of "hypothetically safe languages are safer" (which tends to be the talking point ad nauseam in language flame wars) to "we've established theory that safe languages empirically result in fewer CVEs by at least 65%". Safe Rust doesn't really let you introduce concurrency bugs either, which you admit show up even in well abstracted C.
- jstimpfle 5y ago> Safe Rust doesn't really let you introduce concurrency bugs either, which you admit show up even in well abstracted C. It was really obvious stuff that was immediately reproduced though. Related to a refactor taking into account some learnings that restricted concurrency almost to a single implementation file. My conjecture is that if code is littered with synchronization primitives, the structure is wrong. A system that automates the painful part of getting the use of synchronization primitives right is possibly rewarding bad structure, at least in the short term.
- jmull 5y ago> In conclusion, the empirical research supports the proposition that using memory-safe programming languages for these projects would result in a game-changing reduction in total number of vulnerabilities There are some missing pieces here. A software rewrite introduces a lot of new bugs. Take one of these OSs. They’ve been fixing security bugs for decades, ~30% of which are non-memory safety related. A rewrite in a truly memory safe language will have zero memory safety-related bug but will include a substantial number of new, non-memory-safety security bugs, the kind it took decades to remove from the original code. I think there would be a long term payoff, but considering the resources required, it’s unclear if a rewrite effort could even succeed at all. Realistically, you’re going to end up forking, with some development happening on a rewrite fork with low feature velocity and other development happening on a higher feature velocity fork with no rewrite effort. With low features and years with actually more security bugs, I’m not sure the rewrite fork would even survive. “using memory-safe programming languages for these projects” is easy to say, but what’s the actual path that can be followed from here to there?
- AnimalMuppet 5y agoWhat you say is true, and I'm not sure that "rewrite everything" is a valid approach. But if you're starting something today, you might take this data into account when choosing the language...
- ExtraE 5y agoYes. I don't think rewrite everything was what the original author was arguing for, either.
- AnimalMuppet 5y agoBut someone will argue for it, so the warning not to is worthwhile.
- pylua 5y agoI principle I agree with this . However , if companies really believe there is zero tolerance for security issues , the only way forward is to, no matter the cost, proceed forward with a way that will minimize security problems. Otherwise we are just paying lip service to security .
- thidr0 5y agoAt least in C++11 and later, many classes of these memory bugs are eliminated with more modern container and pointer types. It’s not uncommon to have a company policy of not using “new” or “delete” anywhere.
- pclmulqdq 5y agoEquating C with modern C++ is a common sleight of hand for rust evangelists. Most modern C++ projects with a fresh codebase have almost 0 use of new or delete. It turns out that C++ is a lot better than it used to be 10 years ago.
- est31 5y agoC++ is definitely better but it's still not memory safe. Compared to Rust, you still have little tracking which thread has access to which variable at which time. Even in modern C++, you still have to care about iterator invalidation.
- mhh__ 5y agoIt's far from done, but the GCC static analyzer actually can find iterator invalidation!
- UncleMeat 5y agoSome of it. Because a sound analysis would throw FPs up too frequently, they make the logical decision to use an unsound analysis. This is helpful, but cannot prevent the entire class of issues.
- elteto 5y agoWhile unique/shared_ptr alleviate some of these issues the STL is still full of UB though, and that can’t be fixed easily.
- mcguire 5y agoWhat proportion of C++ code is "modern"?
- einpoklum 5y agoYou can tell the author will not address the matter seriously already from the title: First, talking about "Science" in general seems like grandstanding; and it immediately becomes clear that the author is citing some industry surveys regarding people's perceptions of the cause of bugs. So it's not like "Science has spoken". Second, C and C++ are very different languages. Thirty years ago, you could have put them in the same basket; but times have changed. C++, with its standard library, has advanced to a point where issues such as use-after-free and double-free disappear - not through programmer discipline, but by the programmer simply not allocating and freeing memory themselves. See this post: "Why doesn't C++ have a garbage collector?" https://stackoverflow.com/a/48046118/1593077 https://stackoverflow.com/a/48046118/1593077 Moreover, when you're dealing with low-level and system software, especially OS kernels, it is usually either impossible or impractical to use a higher-level language, with a large interpreter or an even-larger virtual machine. And when the "safe languages" are used for such tasks, one often goes into unsafe mode, like Java's JNI. Now, it's quite possible that using Rust in more lower-level settings will result in fewer memory bugs. But the author has overreached with a flamebait of post and a claim.
- pjmlp 5y agoIdeally yes, unfortunely too many people keep writing C+, not C++. Trying to prevent it seems to be a quixotic endevour other than migrating to something else (doesn't need to be Rust), unless C++ vendors finally adopt some kind of -fvintage-c++=off compiler flag.
- einpoklum 5y agoWell, the C++ coding guidelines project is focused in particular on static analysis measures, and there is quite a bit of collaboration from IDE developers (not sure about compiler authors). So, a "vintage C++ off" is actually less unrealistic than you might believe. In particular, I see IDEs and compilers easily shifting to making it annoying for people to make raw allocations for example. Still, point well taken about people tending to write "C+". They then also complain about how modern C++ is complicated and they can't "see what's going on" etc.
- abainbridge 5y ago> I posit that the second set stays the same size: there’s no reason or evidence to think that porting C++ to a memory-safe language results in additional SQL injection. I think I disagree, although I prefer C to C++ these days. I posit that memory safe programming languages are more complex than C and sometimes cause code that would be simple in C to be written in a more complex way to allow the memory safety to be maintained. I posit that complexity is one of the causes of security vulnerabilities.
- ksml 5y agoCan you give an example?
- abainbridge 5y agoI can't think of a good one. Graph-like data structures is a common example of something that's a bit difficult in Rust - see https://stackoverflow.com/questions/34747464/implement-graph-like-data-structure-in-rust https://stackoverflow.com/questions/34747464/implement-graph.... But the answers there seem reasonable and I'm not a Rust expert. I guess my point is that nobody would even need to ask this question if the implementation was in C.
- jart 5y agoAlex Gaynor is a flamer. He shouldn't be dragging the good name of science into his language flamewar. His opinions on Rust aren't a science any more than Dianetics or Dialectical Materialism is a science. Alex Gaynor is also the reason we're now forced to install Rust on our computers in order to use Python, if we want to share our code on PyPi.
- johntortugo 5y agoI tend to agree with you. Some people add "Science" to the title just because 1) there are some kind of statistics on the post and/or 2) to make the post more appealing.
- Veserv 5y agoThe problem with this sort of analysis is that they are discussing improvements to average commercial software, but, from a security perspective, average commercial software is atrociously terrible and grossly inadequate in any non-trivial security context. To achieve a game-changing reduction in the total number of vulnerabilities and make them barely adequate would require improvements on the order of 10 to 100 times. A mere 100% improvement does not even move the needle when you need to improve by 10,000% to get to acceptable. It is far more reasonable to look at the existing security-critical systems and designs that already achieved outcomes 100 times better than average commercial software such as those certified to Orange Book A1 and identifying the high ROI improvements that can be cost-effectively retrofit to lower security designs. This is far more likely to result in good outcome than stacking 30% improvements onto a completely inadequate foundation in much the same way it would be easier to retrofit a luxury car design to create a cheap car than to enhance a go-kart design.
- pjmlp 5y agoAverage commercial software is what 99% of developers get to write, the amount of places for the mythical elite 10X developers that always do everything perfect is very tiny.
- mamcx 5y agoI work in this space, and agree with "average commercial software is atrociously terrible and grossly inadequate" not just in security but any metric you can think off. However, this is the thing: A lot of security is related to improve in how deal with your inputs/outputs. And a lot of how you improve that is to make better designs in your classes/structs/enums/functions/etc. In a language like Rust, people think the "borrow checker" is the only(major?) reason of the improvements, but working in this niche, just the fact I must model everything in terms of structs/enums/traits have "fixed" tons of stuff without even trying. And I get for free faster execution and half-resource consumption (or less!) that lower the bills! Yes, still need to worry about security and safety, but the internals get nearly fixed by consequence of the design choices of Rust. For me, the borrow checker is a small thing in the whole picture, and is instead the rest that pay off much more ROI.
- quelsolaar 5y agoBuffer overflow is not a bug, its the consequence of a bug. Unsafe languages don't inherently have more bugs, they have more dangerous consequences of bugs. If you want to reduce the consequences, there are a number of things you can do. If you want to reduce the number of bugs, you should look at what the cause of the bug is, not the consequence. C is a small language about moving stuff around memory with pointers, so the consequences of bugs are commonly going to be a wrong read/write. That fact says very little about what the mistakes actually are.
- oscargrouch 5y agoI pity companies that will follow this hype train and rewrite perfectly working software to achieve nothing in the end, from a pragmatic point of view. You can do an essay for every other vector of software development and claim how a rewrite will do you wonders: Eg: FP, strong type system, Linux... The problem is, in real life, we have a bunch of a problems to solve from many vectors, not just (the little square) of memory safety related bugs. And people from the real world tend to measure those vectors together for their particular problems and often their answers, even if they go for C++, are a better suit to solve their issues than a objective-imperative from a person who looks only from one perspective, and think that is 'the most important thing®' that should be pursuit, risking avoidable financial bankrupts (by threatening financial bankrupt). Think for a minute if the native access of a giving library, say, LLVM or Tensorflow is much more important for the project and/or the company survival.. Its like Bush's "War on terror" making people feel fear about some boogeyman but actually inflicting the real terror they said you should trust them to avoid. Specialized, cartesian division of technology tend to turn us all into neurotics, and the problem with neurotics is that they don't see the bigger picture which is much more complex and systemic as things in real world tend to be. Contrary to the extremists views, modern C++ is a fine option and people should not go for this FUD tactics, making they fear to opt for a perfectly nice platform to develop things, and that has proved itself over and over through time doing amazing things. Rust might be a fine option too, but this whole urge to rewrite C++ software is pure non-sense. https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/ https://www.joelonsoftware.com/2000/04/06/things-you-should-...
- vzaliva 5y agoThe `eval` part is total rubbish. The author draws a connection from memory safety to eval. For example both Rust and Haskell, are "memory safe" languages without `eval`. He is probably thinking about intepreted languages like Lisp.
- account4mypc 5y agoI wonder if there is another way to partially explain these results: - many (most?) programmers were taught C/C++ for several decades - some programmers taught themselves rust because they were driven and interested - these rust programmers are probably better programmers on average - they wrote (or would write) better code that is somewhat elegant. and they have the advantage of starting with code that has had its memory bugs fixed over the last few decades So I wonder if you had taught the masses rust starting in the 1980s... would the masses have found other way to write buggy/crappy code? (I realize this could sound a bit condescending; fwif I did not teach myself rust so i'm not counting myself in the 'better' group)