Hacker News new | ask | show | jobs
by paulmooreparks 2617 days ago
Why is it difficult to believe? I've also written plenty of C++ code without memory bugs. It's not that hard if you play by a few simple rules.
2 comments

> I've also written plenty of C++ code without memory bugs.

The classic response to this is "That you know of." Consider that even quality-conscious projects with careful code review like Chrome have issues like this use-after-free bug from time to time.

https://googleprojectzero.blogspot.com/2019/04/virtually-unl...

So when people claim that they personally don't write memory bugs I tend to assume that they are mistaken, and that the real truth is that they haven't yet noticed any of the memory bugs that they have written because they are too subtle or too rare to have noticed.

Chrome is in an exceptionally hard place because of its JIT. Your language cannot tell you if it's safe for your JIT to omit a bounds check.
That post describes two vulnerabilities: one is in the JIT, but the other one is in regular old C++ code. More generally, JIT bugs are a relatively small minority of browser vulnerabilities. More often you see issues like use-after-free in C++ code that interacts with JS, such as implementations of DOM interfaces, but the issues are not directly JIT related and would be avoided in a fully memory-safe language.
Chrome, like Firefox, is not an example of modern C++ code. Google's and Mozilla's coding standards enforce a late-'90s style. It is astonishing they get it to work at all.
In this case, I mean a subsystem that has been in production since 2006 and has been processing hundreds of thousands of messages a day. I don't claim that it's perfect or bug-free, but if it had significant memory errors I'd have heard about it. I designed and implemented it to use patterns like RAII to manage memory, and it's worked quite well.
That is why use tools like valgrind to verify that you got it right.
When I worked on a mobile C++ project at Google, we went exceptionally out of our way to avoid memory issues.

We ran under valgrind and multiple sanitizers (and continuously ran those with high coverage unit and integration tests). We ran fuzzers. We had strictly enforced style guides.

We still shipped multiple use after frees and ub-tripping behavior. I also saw multiple issues in other major libraries that we were building from source so it can't be pointed at as just incompetency on my team.

Basically, it might be possible but I think it's exceptionally more difficult to write memory safe C++ than this thread is making it sound.

Writing memory safe programs in C++ is possible. Most coding styles and some problem domains don't lend themselves to it naturally, though. In my experience, restricted subsets used for embedded software vastly reduce the risk of introducing errors and make actual errors easier to spot and fix.
> Writing memory safe programs in C++ is possible.

Everything "is possible" in the sense that in theory you can do it. But if time and time again people fail to do it. Even people who invest almost heroic levels of effort (see above: valgrind, multiple sanitizers, and so on) you get to the point where you have to accept that what is possible in theory doesn't work in practice.

I have seen it done in practice, on rather large systems. But it requires actual, slow software engineering instead of the freestyle coding processes that are used in most places.
Yes, I know how you code are obliged to code at Google. It is astonishing that anything works.

The "strictly enforced style guides" strictly enforce '90s coding habits.

Together with a test-suite that covers the exponential number of paths through your code...
Changing programming language neither reduces the need for test coverage nor does it magically increase coverage.
A type system changes the need for test coverage because it eliminates whole classes of bugs statically that would need an infinite amount of tests to eliminate dynamically.
That leaves an infinite amount logic bugs to be tested for. Types cannot fix interface misuse at integration and system level. So no, this does not reduce the need for testing.
I haven't written c++ seriously for a number of years. Do you still have to do all that "rule of three" boilerplate stuff to use your classes with the STL? Is it better or worse now with move constructors?
It's a bit better with C++11 syntax where you can use = delete to remove the default constructors/destructors, e.g.:

  class Class
  {
      Class();
      Class(const Class&) = delete;
      Class& operator = (const Class&) = delete;
      ~Class() = default;
  };
Which I find slightly cleaner than the old approach of declaring them private and not defining an implementation, but the concept hasn't changed much. I'd love a way to say 'no, compiler, I'll define the constructors, operators, and destructors I want - no defaults' but that's not part of the standard.

Move constructors are an extra that, if I remember correctly, don't get a default version, thankfully.

So, so much better. Nowadays we "use" what has been called "rule of zero". Write a constructor if you maintain an invariant. Rely on library components and destructors for all else.