Add a static analyzer pass to each C/C++ compiler which is switched on by default. Add clang-address-sanitizer-style code when compiling in debug mode, and also offer runtime checks as option for release-compiled code. Both combined would have caught 99% of memory safety issues that pop up now and then.
If necessary extend the language with optional ownership keywords a'la Rust, and allow me to switch off features I don't require to reduce complexity.
Most important of all: provide options that I don't need to pay at runtime for memory safety with precious CPU cycles.
I don't understand this type of reaction every time the problems of C/C++ are highlighted. Yes, of course the problems can be migitated using tools, but clearly this is not being done or is not as thorough or practical a solution as making it the language's responsibility. There's hoops that you must jump through to make C/C++ safe and clearly this just doesn't happen in practice. The same hoops just don't exist for Go and Rust (and that's just the system(ish) programming languages) and therefore I disagree that you can handwave this away as bullshit.
Rust is the right approach since it figures everything out at compile time. But it still has to prove itself. And what is the timeline for a browser completely re-implemented in Rust? 5 years? 10 years? Suggesting that throwing away and rewriting our entire software foundation that has been written in the past 50 years is just silly. To be realistically achievable we need an incremental approach to gradually analyze and fix old software instead of throw-away-and-rewrite.
Luckily, with Rust's great FFI, it's easy to peel off a component and re-write it, you don't need to do the whole thing. It's not like Firefox is getting transpiled to Rust tomorrow, we're just replacing bits and pieces, slowly.
Well the time will come when you will realize that
1) every language has hoops to jump through to make them safe (e.g. garbage collection does not protect you from memory leaks) [1]
2) every 4-5 years or so someone comes out with a programming language/system* [2] (rust, go at the moment) that fixes it all and "encourages good practices" - and then the good practices change (e.g. microservices are making their second round. They're good for sysadmins/sres and very bad for programmers (because you have to serialize/deserialize everything and changes to the system propagate into potentially dozens of separate programs - and God help you if they're maintained by different departments)). I wonder what's next ? Data-oriented programming is the perfect solution for the problems of microservices, so that could be next I guess.
* they may call it programming language, they may call it a system. Of course, it's always both.
[1] https://www.youtube.com/watch?v=ydWFpcoYraU
[2] Forth, C, COBOL (/mainframes), Pascal, C++, Oberon vs Modula-2, dBase (and it's competitors), Object Pascal/VB (the built-in database approach), Perl, Java, .Net, and now here Go/Rust
No need to panic, nobody is coming to take your precious pointer arithmetic away. If you want to write unsafe code, go ahead. But we also have to acknowledge that we as a profession are not able to write secure code in C/C++. We're talking about classes of security bugs that just don't exist in managed languages. Bugs that can't happen in languages like Rust either because it forces programmers to indicate ownership and lifetime of memory.
There's always a trade-off between performance and security, but with better tooling support the price for secure code will be very low. Right now security is bad because we look at security as a feature. As something that is added to a project, instead of as something that guides the entire design.
Your suggestion that static analysis can fix this is wildly optimistic, because C/C++ code for a modern browser uses memory pools, pointer arithmetic, just-in-time compilation, and shared memory between object instances, threads and processes and so on. It's hugely complex, and correctness can't be verified with some sort of static analyzer or with added runtime checks.
> But we also have to acknowledge that we as a profession are not able to write secure code in C/C++.
Wrong. You can write perfectly safe code in C++. You seem to have this ridiculous idea that pointer arithmetic is mandatory on C++. "Pointer arithmetic" is not a feature of C++. It's a feature of how computers work. C++ allows you to do it if you want. Memory pools are not a C++ feature, memory pools are part of how a modern computer and OS works. You know, memory doesn't appear magically in your program.
> Rust forces programmers to indicate ownership and lifetime of memory
So like C++ with unique_ptr, shared_ptr, and RAII?
Also I wanted to say that some of you keep mixing C and C++, calling them "C/C++". They're two different languages.
shared_ptr uses an atomic counter. In rust you can choose to use an atomic ref count or not. The type system is sufficiently smart to ensure that you do not use the non-atomic one in a thread unsafe context. C++ has no ability to do so, so it does not provide a thread unsafe version.
Additionally, in C++ there's nothing stopping me from wrapping a unique_ptr around a pointer that's aliased else where. This violates the guarantees of a unique_ptr and forces the user to make sure its invariants are held. In Rust the compiler ensures that your Box (Rust's unique_ptr) can never be owned more than once.
>C++ has no ability to do so, so it does not provide a thread unsafe version.
In which case would you like to enforce a thread unsafe version?
>Additionally, in C++ there's nothing stopping me from wrapping a unique_ptr around a pointer that's aliased else where. This violates the guarantees of a unique_ptr and forces the user to make sure its invariants are held. In Rust the compiler ensures that your Box (Rust's unique_ptr) can never be owned more than once.
True, nothing stops you to do that and it is nice to know it on compile time. But I fail to say how you would do this by accident. If you deliberately want to do this kind of things, then you are asking for trouble and it doesn't matter how awesome your language is, you would find a way to shoot your foot.
> In which case would you like to enforce a thread unsafe version?
Any time you don't use shared_ptr in a multithreaded context, you're paying extra for the overhead of atomics. In Rust, if you need shared ownership, but you're not doing anything with threads, you can remove that overhead. If you then later try to use it across threads, the compiler won't let you, until you switch to using Arc.
> In Rust, if you need shared ownership, but you're not doing anything with threads, you can remove that overhead.
If I am following you well, then what you are saying is that you if you are in a single thread application you don't need to use a shared_ptr equivalent. Wouldn't that mean that you are using a garbage collected pointer? (Which should have a greater overhead than a shared pointer) because otherwise you would be using plain old raw pointers (with all its problems).
Well, it's the same thing, but without the atmoics. Rc and Arc only differ in the instructions used to update the count, Rc uses regular increment, Arc uses atomic increment. The code is otherwise the same.
Arc<T> vs Rc<T> compared to shared_ptr was what I was going to say for overhead, as well as things like the lack of move constructors, which (should) make Vec<T> faster than std::vec.
For stronger guarantees, I was going to point out that if you std::move a uniq_ptr, it becomes null, but Rust prevents using a Box<T> that's been moved at compile time. Your example is good here too.
> the lack of move constructors, which (should) make Vec<T> faster than std::vec.
So you are saying that the lack of move constructors (hence copying the structure) will make Vec<T> faster than std::vector? I don't follow that logic, wouldn't it actually be slower?
> I was going to point out that if you std::move a uniq_ptr, it becomes null
The original becames null because the semantic is actually tranfering ownership which might be desirable depending on the case. Also you need it in case you are using custom deleters.
Specifically, I mean that when the vector changes size, the vector needs to reallocate. In Rust, since there are no move constructors, it's just a memcpy of the whole chunk of memory, one big one. Very straightforward and fast. In C++, you need to call all those move constructors.
> The original becames null because the semantic is actually tranfering ownership
Absolutely. In Rust, this is tracked at compile time, and attempting to use after the move is a compile-time error. In C++, you'll get a null pointer dereference. (I _think_ that technically it's in an 'unspecified' state, not a null one, but on my system, null is what I get).
> mean that when the vector changes size, the vector needs to reallocate
You can control how often the vector changes the size and a lot of times you can even prevent that.
> In Rust, since there are no move constructors, it's just a memcpy.
memcpy is actually slower that move the ownership of a buffer, that's why you avoid copying as much as you can (std::vector also uses memcpy inside).
> In Rust, this is tracked at compile time, and attempting to use after the move is a compile-time error.
It is nice to get a misuse at compile time, but my point is that once you transfer the ownership of the pointer you shouldn't be using that unique_ptr anymore. I'm probably biased but I don't see this "accidental" misuse happening often (tbh I haven't seen it ever and I have seen some horrible things in C++ before)
I don't think I am communicating myself clearly, so I'll just bow out about this issue, until I have an example ready. I should write one anyway, I just don't have it at the moment.
> I'm probably biased but I don't see
this "accidental" misuse happening
> often
While that may be true, as the article points out, all it takes is for someone, anywhere, to slip up once, and then you possibly have a serious security violation. Having the compiler double-check your work is nice.
>But we also have to acknowledge that we as a profession are not able to write secure code in C/C++.
You can't write safe code in any Turing complete language. That's the whole point of Turing completeness. The only reason why memory attacks are as common as they are is because C like languages are the most popular ones.
If we replaced everything written with C to a "safe" language like, I don't know Haskell?, we'd have just as many zero day exploits of the monads within programs.
You cannot write safe code in any Turing complete language? That's a bold assertion that I don't believe is true. Surely you can use formal methods to develop software and maybe even prove it's correctness and security, it's costly in multiple ways but the language of implementation doesn't prevent you from doing this. It is possible to write secure code.
If we replaced everything in C with Haskell, we'd have an entirely different problem. The attack surface wouldn't involve buffer overflows and stack smashing, it would involve various DoS attacks. Those might be easier to address though.
There is a huge difference between a program that computes the wrong answer and one that corrupts memory, hijacks a shell, and installs a rootkit.
Javascript is turing complete but it's trivial to write a (slow) javascript interpreter in Python that allows anybody to run any javascript program without any risk to their machine. No memory attacks possible. To privilege escalation possible. No unchecked stack overflows. The system would be sandboxed and secure. It will just work or fail gracefully.
It's only when we increase the complexity of our runtimes a thousand fold and when we cut corners to squeeze out higher performance that all the nasty vulnerabilities start to creep in.
> If we replaced everything written with C to a "safe" language like, I don't know Haskell?, we'd have just as many zero day exploits of the monads within programs.
I disagree. There will always be security issues, due to (ab)uses that developers didn't consider, but there can be different probabilities. Requiring developers to learn about language gotchas, remember those gotchas when coding, and jump through hoops to avoid them, just stacks the odds against us.
> You can't write safe code in any Turing complete language.
Correct. But, a lot of code is written in Turing complete languages which does not really require any Turing completeness at all. And some code should be implemented in non-Turing-complete, verifiable languages.
Of course you can. You can, for instance, include an automatically verified proof that your code terminates for finite inputs.
This is in fact where the newest academic languages are going. In dependantly typed languages bounds checks are only done in theory. you have to include a proof that your indexes are within bounds, and the compiler verifies that proof. If it checks out, it compiles. If not, back you go. Far safer than java/go and the like and far faster than c++. These languages tend to allow pointer arithmetic too, for beating it in speed is almost impossible.
> But we also have to acknowledge that we as a profession are not able to write secure code in C/C++.
Then you've picked the wrong profession. You write that like it was a fact, while it's obviously nothing more than a lack of skill (and maybe knowledge) on your side.
If I chose a language primarily because it's 'safe', that would mean that either I'm pretty bad at writing safe code, or that I'm just unnecessarily lazy.
Maybe a single developer personally can write C/C++ code that is probably pretty safe (keeping in mind that most personal/small projects never get a proper security analysis/penetration test, so one may have a biased perception), but as soon as you have several people with non-trivial components that interact, it is quite to introduce serious problems accidentally. The evidence is unfortunately against C/C++: even the biggest tech companies in the world developing applications for which security is a critical feature (operating systems, web browsers) still regularly have bugs caused by memory safety issues, and they're almost certainly throwing every tool and a lot of CPU time at the problem.
Assuming you work alone maybe yes. However most of us work in teams.
C and C++ have long proven that with average skills teams, the end result is lurking buffer exploits, dangling pointers, corrupted memory somewhere in the code base, that usually take days to sort out.
To a degree this ammuses me greatly. Rust allows you to opt-out of safety. While your proposal (which I agree with to be clear) allows you to opt-in to safety in C/C++.
This really cuts to the core of ideological differences between C/C++ and Rust camps. C/C++ programmers tell the compiler trust me, while Rust programmers say check me. I can't help but feel to a degree the Rust ideology is superior. To make by bias clear I work in C, and write in Rust in my spare time.
To be clear, I love Rust's approach to safety. I just don't think it is an realistic expectation that we can re-implement all security-critical C code in Rust until the sun goes super-nova ;)
It's probably not going to be a rewrite of existing systems, but a switch to new systems written in safer languages while phasing out the old C based systems.
On the server: unikernels. On the client: new mobile platforms, like OSes based mostly on web technologies (which while not quite being there yet are getting more and more powerful).
C/C++ is not only a poor choice in regards to security, but also to develop for hardware with hundreds or thousands of cores, and all hardware platforms are moving in that direction. Yes there are of course ways to deal with that in C/C++, but other languages are much better at this.
Within just a decade or two I can see a fundamental shift away from the old computing paradigm, at least for the majority of systems in mainstream use.
While tooling may help, I think that a completely new approach is necessary.
The correctness invariants of complex C++ programs (such as browsers and JITs) cannot be 'discovered' by static analysis - they must be, at least in part, supplied by the programmer.
C and C++ were not designed to allow programmers to specify such invariants (and have them automatically checked). I am not convinced that introducing them can be done in a clean way.
Hmm true, we could have a new 'safe' keyword (or even #pragma) which would switch off 'unsafe' language features (basically the opposite of Rust's 'unsafe' enforce a stricter, more restrictive code style which is easier for the static analyzer to reason about). The majority of even high-performance C/C++ apps only needs to twiddle bits in very small areas of the code. That's still a lot better then trying to rewrite basically all software that has been written in the last 50 years ;)
It is used for that exact memory control and access that this article demonizes so that we can have efficient and thought out systems.
When those systems aren't well thought out or secure you have security issues. C/C++ lets you build a wobbly treehouse _and_ a secure fortress. It is up to the developer which one is made...
C and C++ are used due to UNIX becoming widespread, opening the door to those languages in the industry, and killing safer systems programming languages in the process.
The majority of security exploits in C and C++ aren't possible in Modula-2, Ada, Algol, just to cite a few examples.
To quote Hoare on his award's speech, The Emperor's Old Clothes:
"Many years later we asked our customers whether they wished us to provide
an option to switch off these checks in the interests of efficiency on production runs.
Unanimously, they urged us not to - they already knew how frequently subscript errors occur on
production runs where failure to detect them could be disastrous. I note with fear and horror that
even in 1980, language designers and users have not learned this lesson. In any respectable branch
of engineering, failure to observe such elementary precautions would have long been against the
law"
Becoming widespread? Outside the desktop, the most commonly used operating system is Unix and its "likes" and dominates the internet and mobile devices.
A place where your reasoning breaks down is when one comes to define "the developer".
There are a few legendary developers in the world I would trust to write secure C/C++. Others may be able to do so, by luck.
Would I trust an organization of 2000 people all developing the same large code base? I know there are processes that can be adopted, such as using clever static analysis tools and rigorous review by security professionals.
But time and time again, the community says "Yay, we've invented X, this makes a huge class of security bugs obsolete". Then Y comes along and opens the playing field again. (e.g, X=Data execution prevention, Y=Return oriented programming).
This keeps happening. It takes some arrogance to believe that a safe large system can be built in an unsafe language, these days.
"...But time and time again, the community says "Yay, we've invented X, this makes a huge class of security bugs obsolete". Then Y comes along and opens the playing field again..."
To be fair...
This issue exists with every other language as well. I would be very skeptical of any language claiming to be 100% secure.
The same is true of the developer argument. Cyclone, Rust, C#, Java... you name it... they are all capable of producing systems with security vulnerabilities owing to developer quality inconsistencies.
I couldn't agree more. I like how he is very pointed at C/C++ and doesn't mention any of the safer, garbage collected languages....OH NOES, they have issues too!
I can keep going here but you get the point. Perhaps he should have also taken it further and proposed that we move onto Mill processors since the underlying hardware has a history of issues:
Yes in C/C++ you can make mistakes (#gotofail) but in other languages, I would argue you can fuck-up in ways that are not nearly as obvious such that static analysis can't catch. A nicety about C/C++ is the tooling has gotten pretty damn good over the last 43 years (clang, gcc, valgrind, etc) and can catch most of these kind of bugs. The onus is on the developer to do the correct thing though and it appears that over at Adobe, etc that they (historically) could give 0 shits about doing this. It honestly seems to be though, that they are tightening up things.
Perhaps we should rebuild everything from the ground up....
Hmm - the Java attack seems to assume that a JMX server has not been configured to authenticate requests, can be connected to using RMI and has no class loader security manager.
I'm not sure this is really a problem with the Java language, or even the programmer. If I leave my front door wide open, I don't think I can blame the lock for failing to do its job.
There are no secure systems written in ANY language (to my knowledge).
At the end of the day all computers and systems can be broken. Using C/C++ might open up that up to being more likely when someone isn't thoughtful enough in the implementation. The trade off comes in when you want to risk the higher chance of security problems for the better performance.
This is just my opinion, I believe that we spend way too much time writing in these 'safe' languages at the cost of performance when it often isn't really needed and in the long run just adds to the bloat of our systems. This comes from my belief that if anyone is willing to spend the time and money to break a system they will. So why are we burdening our piggy banks with the same security as our bank vaults?
So what language to you propose we use to rewrite all video and audio software? Javascript, Lua, Brainfuck, what? Sure make every numeric value a double, nobody ever wants a char. Don't allow contiguous blocks of memory. Or if you do check every access is within the bounds. Its all safe! Nobody cares that suddenly we can't decode an MP3 in realtime.
> So what language to you propose we use to rewrite all video and audio software? Javascript, Lua, Brainfuck, what?
It's not proposing a particular language; only a property was proposed: memory-safety. This was defined loosely:
> Garbage collection was invented in 1959, for Lisp, the first memory-safe language. It provides the foundation for memory safety and it is used in most memory-safe programming languages today. (It is also possible to have memory-safe languages without garbage collection, e.g., Cyclone or Rust.)
We could compare this with other properties of programming languages which have been tried in the past, but are now considered bad ideas or a last resort:
- DWIM (Do What I Mean): if a name (function, variable, etc.) doesn't exist, use one with a similar spelling
- Ignore errors by default: eg. Bash without "set -e", visual basic's "On Error Resume Next", etc.
- GOTO: still useful on occasion, but no longer the "go to" control structure in most code (pun intended)
- Dynamic scope by default
- NULL: Perhaps controversial, but also infamous. Can be tamed with nullable types, or eliminated with option types.
- "Loose typing", ie. the casting rules in PHP, JS, etc. which break symmetry and transitivity of "=="
- Automatic semicolon insertion: Either require semicolons or don't; trying to guess leads to problems
- Name-based typing: eg. in FORTRAN any variable name beginning with I, J, K, L, M or N are implicitly ints, anything else is implicitly float
I would certainly consider memory unsafe languages in the same category. There's no need, now that we have garbage collection and linear types.
I suppose the answer would be Rust or similar because they aim to be as fast as C/++; however, that is currently only a goal and most certainly not the reality (according to some source I read a month or two back that I can no longer find).
I also hope to end C/++, but it's simply not time yet.
2) Which attack surface would you rather deal with... the small fraction of your graph library that deals with mutability... or all of Adobe Flash?
3) The fact is that "unsafe" doesn't mean unsafe, it means "trust the programmer that this is safe". It's reasonable to assume that safety can be maintained in Rust libraries that use the unsafe keyword.
It seems to me that an undirected graph can be represented as a map from keys to sets of keys (which can be integers, strings, or anything else), with the invariant that map[x] contains y iff map[y] contains x. For a directed graph, use two maps for incoming and outgoing, with the invariant that incoming[x] contains y iff outgoing[y] contains x.
This approach doesn't require unsafe anywhere and all graph operations are easy to implement, including deleting nodes. Am I missing something?
This argument is very flawed, since you are forced to use `unsafe` when you need anything communicating to the outside world (which includes, obviously, `println!`). The very point of Rust is to limit the unsafe surface, not to completely eliminate that.
First of all a graph library does not communicate with the outside world.
My point was that even for the ubiquitous task of implementing a graph structure, unsafe is necessary.
So while Rust may provide a clean separation between unsafe and safe code (enforced by the type system), the original problem remains: How do we ensure correctness of the unsafe parts of the code.
For what it's worth (and I intentionally didn't point this out in the parent), you can make a safe graph library in Rust with a typed arena (slightly less ergonomic and faster) or a refcounted smart pointer (slightly more ergonomic and slower). But this still does not validate your point, since the memory allocator is in many cases unsafe.
On how to ensure correctness of the `unsafe` code: that was what we were doing with the entire C/C++ code for decades, so what's the problem? We could however concentrate on the much less amount of code if we were using safer languages.
I've never actually used Rust myself - I'm just familiar with their goals: so I'm not entirely sure what you are referring to.
Just keep in mind that the amount of work done on the various C++ compilers is most likely measured in man-decades, where Rust is probably still man-hours.
Do you mean a directed graph or a graphical graph? I've implemented directed graphs in at least two different managed languages (which are more constrained than Rust) and had to use no unsafe breakouts. There might be some complexity for some reason with Rust, but if it's possible in a managed language then surely...
It's nowhere near the amount of time put into various C++ compilers, but
1. We use LLVM, so all that time is working for us as well.
2. Mozilla has been paying at least 4 people for at least a
few years to write Rust full-time, I would bet we're coming
up on a person-decade of time for Rust. The project has existed
for eight years in total, though four of that was just as a side
project.
> (according to some source I read a month or two back that
> I can no longer find).
Stuff changes fast in Rust-land, so even if it were a month or two, it might be wrong. We should always be pretty close, and we're sometimes faster, depending on the code.
I think the main reason we aren't moving away from C and C++ is interopability, and not performance. I can come up with a number of benefits of C in this regard:
1) Ability to use existing libraries directly (just #include and add to LDFLAGS).
2) Ability to be called (as a library) by code written in almost any language, without indirection through serializing function calls or similar.
3) Modern operating systems can share code and constant data sections between processes for normal compiled programs (but not byte-compiled ones).
4) C has a good tool support, such as debuggers and packaging tools for Linux distributions.
5) A practice of backwards-compatibility in the C language and libraries written in C makes it possible to mix code that was written for different versions of C or that use different versions of the same library.
The question is about defaults. Current default of C and C++ is to cut all corners wherever it is not clearly impossible (sometimes even then). That is the insanity.
Ugh... these kinds of extreme posts really piss me off. Yes, C is unsafe. Yes, there are safer higher level languages, but there's a reason C is used. Because it's REALLY freaking fast, and allows you to actually tune how memory is used.
The issue with C is not the language itself. It's the complexity of the project. Once a project reaches a certain size, no programmer will be able to keep the entire thing in their head at one time. That's why the bugs appear.
I have no issue with saying that C is hard to work with in giant projects. If you have an issue with that, write the sections of code that bottleneck in C, and then glue them together with a higher level language (I personally like Lua for this).
Also, the post is mainly complaining about security issues for Chrome. Assuming Chrome is rewritten in Rust or Go, will these security issues vanish? Of course not. Bugs (especially security bugs) will exist in all software, whether it's written in C or Ruby.
So the results of rewriting all C code in the world is: slower, less optimized code and continued existence of security bugs. Sounds great.
> Assuming Chrome is rewritten in Rust or Go, will these security
> issues vanish? Of course not
It's true that they will never _vanish_, but all of the ones related to memory safety, which are the ones the article focuses on, shouldn't happen in Rust:
> 70% of the high-risk bugs in Chrome 44 would have been prevented
> if Chrome were written in a memory-safe language instead of C/C++.
(it's true that it's not 100%, because someone could still write bad code in an unsafe block, but that's a _significant_ reduction in the surface area, which should lead to a similar reduction in errors.)
"It is crystal clear for every sane programmer that C/C++ is not going to die in the nearest future. No one is going to rewrite almost all of the existing desktop applications, operating system kernels, compilers, game and browser engines, virtual machines, databases, archivers, audio and video codecs, tons of other C-libraries, and so on and so forth, into other languages. This is a huge mass of fast, debugged, and time-proven code. Rewriting it is way, way too expensive, risky, and, honestly, doesn't seem to make sense except in the heads of the most frantic Rust fans. The demand for C/C++ programmers has always been high and will remain so for a long time to come."
Seems like just more proselytizing from Rust fans looking to expand their ranks. Of course more people in the security industry are not taking up this call, because it is a ridiculous suggestion.
Another way to look at it: the ones that can write that software are still chosing C/C++. The ones who would like to use Haskell/Rust/Java are discussing online.. :)
C still got a right to exist even in the very sensitive mission-critical environments, as long as MISRA requirements are followed (and, the good thing is that they can be automatically enforced).
Secure software can be written in C or C++ (they are two different languages BTW). For example, OpenBSD and OpenSSH. Both are written in C and both have very good security records.
To this, I can only agree. I love C and I can write secure software in it, but the costs of doing that are generally not worthwhile in today's market (if they ever were).
C will still have a place in its niche, but it shouldn't be considered the standard general-purpose language any more.
Our processors have gotten so fast we can run slow-motion versions without even noticing (smartphone & tablet CPUs), so don't tell me anything other than C would be “too slow”.
C is unbeatable as long as riding rodeo on your raw CPU is all you could ever want. But software has evolved, mankind's dependency on software has evolved, and so have our expectations & standards. It's time to learn the lesson and move on.
Memory-safety doesn't make a language slower. Garbage collection does, but there are other approaches like linear types which perform all checks at compile-time, so the resulting code doesn't need to do any checking/indirection/etc. and is hence equivalent to what C would produce.
Niche as in “where it makes sense to use it because the strengths of the language support your important goals and the weaknesses don't kill you” as opposed to using it because it's one of the most used languages in the world.
The article claims that the problem with Flash is not so much Flash itself, but that it's written in C/C++. It "proves" the point by listing a bunch of high-profile exploits in various other systems, then repeats the claim that C/C++ is the problem, and advocates abandoning C/C++.
That's... not exactly proof. It's barely even evidence.
Yes, you can argue "all these programs have all these flaws, and all are written in C/C++". And that's true. They are written in C/C++, and they have these flaws.
Now let's look at the programs that do the same kinds of things, and are as widely used, and are not written in C/C++, and compare the number of exploits... oh, you don't have a handy list of equivalent programs to compare? Go work on that; I'll wait.
The fundamental problem is that these programs are trying to safely handle hostile input, with attackers having essentially infinite time to experiment and craft attacks, including attacks that the authors didn't know that they needed to defend against at the time the code and/or the spec were written.
Would the whole world be safer if everything were at least written in Java? Yes, it might be safer, but not safe. And it would take more resources, and often take longer to start up. And it would be vulnerable to bugs in the design and implementation of the JVM, which would become a single point of failure for a lot of software. That world would, perhaps, be somewhat safer, but it wouldn't be the security paradise that the article envisions.
Not everyone is a fool who doesn't use the tools you think are best...
I think a lot of comments in this thread are too harsh. I am a C programmer myself and see the value of it in embedded systems, operating system kernels etc.
While it is possible to write correct programs in C and C++ (which are very different languages, but usually mentioned together because of their names and history), it is too easy to make mistakes that are not caught by the compiler. It is easier to be sloppy in C than in say, Go or Rust. I have seen this in my own code and also in others' code.
C is not the only way to write system programs. Oberon, Inferno are good examples of OS environments written in safer languages.
But I agree that some of the disadvantages of C are also its advantages.
The author of the blog post has more followup posts:
C++11 is pretty "memory safe". But obviously it allows the programmer go low-level and deal with dangling pointers if he/she wants. After all, the programmer/engineer is supposed to be a professional that can handle these things and make autonomous decisions.
> It is not even clear how a safe language that would permit this would look like.
If this hypothetical browser is implemented in a safe language that runs on a JIT-compiled environment itself, then it could compile JS to that same safe language and allow that same runtime environment to JIT-compile the resulting compiled JS.
Would that make sense? (I have no empirical data to back this up, but i'd like to know if at least hypothetically this approach could work)
I don't think the author fully appreciates just how huge, and how many new bugs would be introduced by rewrites on that scale. It might be reasonable to say C/C++ should be deprecated for new projects, but truly vast amounts of C/C++ will remain with us for the foreseeable future. Perhaps a better approach would be to ease integration of new code written in memory-safe languages with older code written in C/C++, which tends to be tedious at best nowadays.
C++ is safe enough, provided you use the tools like Intel Inspector, valgrind, and static analyzers. With C++11 memory corruptions became a somewhat rare occurence.
I'm confused when the author says the DOM is garbage collected only in IE/Blink. How is DOM memory managed elsewhere? At first guess, I thought the JavaScript runtime (which surely always uses garbage collection) would manage the DOM, but I'm guessing that's not actually the case. Who is typically in charge of memory management at that layer?
There are a number of hobby projects, but nothing you'd want to use in production yet. That of course doesn't really change the amount of assembly needed.
I do appreciate a good troll once in a while but they shouldn't rise to the top page of HN. It gives them ideas above their station.
I have a burning question tough, What pray tell safe language would you use to write the original garbage collector, because Garbage collecting is only a form of abstracting good pointer practices away, helping programmers write complex programs easily.
Absurdly absurd ideas and ramblings. I suppose we can write our "safe languages" in "safe languages" then we'll never have need of really programming anything ourselves. We can simply pat together what ever is "safe" for us to do in our little play sandbox and act like we're adults who understand the problems faced when programming in "real" languages.
This would be similar to replacing all automobiles with stuffed animals because, who in the world ever heard of stuffed animals getting into pile-up wreaks on the freeway and causing death misery and misfortune.
Rust and servo are great projects, but I would say there's still some time before Rust can completely replace C/C++. The language is stable, but there's still a lot of tooling that needs to be built up, and the compiler needs to be more performant. I also think it needs a few more small features in order to truly compete with C++ (it's already on par with C, in my opinion), e.g. being able to specify sized traits as return types. Once that's all done I think starting new projects in C/C++ wouldn't make sense anymore.
I'd be delighted to see one. Statically pre-allocating a large continuous chunk of memory for the whole browser process lifetime would have been great.
And then, what do you do with that chunk? Since the amount of memory you need depends on the website (e.g. some might need memory for a canvas, others might display large tables, etc.), presumably you take parts of it and allocate them for the various tasks you need to do? So you just write your own dynamic memory allocator.
> So you just write your own dynamic memory allocator.
If the entire pool is reclaimed once page is rendered (and there is a way to handle the running out of space condition) it may not count as a rule violation, no more than incrementing an array index counter.
And, browser is hardly a mission-critical appliance.
My problem with this article and its statement is that it's saying to "sunset" C/C++ because it causes problems but the problems are caused by an incompetent user, not C.
I see so many articles complaining about C letting you shoot yourself in the foot but no one who would shoot themselves in the foot should be handling a gun.
"You know you wouldn't have so many splatters on that painting, Mr. Pollock, if you would just let a computer handle that brush for you!"
It's not the language, it's the tools.
Add a static analyzer pass to each C/C++ compiler which is switched on by default. Add clang-address-sanitizer-style code when compiling in debug mode, and also offer runtime checks as option for release-compiled code. Both combined would have caught 99% of memory safety issues that pop up now and then.
If necessary extend the language with optional ownership keywords a'la Rust, and allow me to switch off features I don't require to reduce complexity.
Most important of all: provide options that I don't need to pay at runtime for memory safety with precious CPU cycles.