Hacker News new | past | comments | ask | show | jobs | submit login

> I could equally well say that there's no "sane" way to implement a safe owning pointer in C++

unique_ptr + std::move gives you that semantically. Sure it won't stop you from dereferencing a null pointer but, in all the years i've been writing C++, finding and fixing null pointer dereferences wouldn't rank very high on my list of things to worry about. They always kill your program and are easy to spot in an IDE or debugger.

Rusts choice to make pointers either mutable and owning, or shared and immutable and garbage-collected, is no doubt the right one, but there are code-styles in C++ where this can be achieved with a very low fuck-up rate.

The modern C++ way is not to use pointers, except as an implementation detail. A pointer (raw or smart) of any type, other than perhaps char*, as a function parameter is a sure sign of code smell, and raw pointers as data members have very limited use.




Unique pointers provide no protection against use after free, because you can take a reference to their contents and that reference can become dangling. Because the destructor of a unique pointer is invoked automatically per the language rules, as opposed to in C where an explicit call to free is required, this makes C++ more prone to UAF than C.


It's unusual to take references to the contents of a unique pointer. There is one idiom which says that if one has a smart ptr and a function taking a ref, the raw ptr should be passed, but that's it. It's frowned upon... nay scoffed at to store references one receives as parameters, so that temporary ref will go away after the function call, leaving the smart ptr as unique owner.

This should not be a problem and it certainly doesn't make C++ more prone to use after free. Null pointers are the problem.

Both can be solved though by creating e.g. a safe smart ptr which does null checks and only exposes operator->.


> It's unusual to take references to the contents of a unique pointer.

No, it's not. It happens every time you call a method on the referent (well, OK, this is technically not a reference, but it doesn't matter to the argument).


One is not taking the reference. When writing ptr->foo() the ref is temporary, not accessible and will be cleaned up when the method call on the raw ptr finishes.

Taking the reference would be "auto foo = ptr.operator->()". This could be forbidden by not providing op->, and instead having an apply function which takes a method name and the parameters. That would be safer, but probably too much effort for little gain.


If you're calling a method on the object referred to by a unique_ptr, then you won't have a use-after-free because the thing will exist. The only way it wouldn't exist would be if you typed "delete myuniquePtr.get()", which would be dumb.

It could be a null unique_ptr of course, but I don't see how this is anything worse than a denial-of-service.


No, you could get a reference to the container of the object and indirectly delete it. For example, if the unique pointer were part of a global std::vector, clearing the vector would invalidate the this pointer.

Keep in mind that you are at this point arguing against the existence of actual zero-days that have occurred in Firefox (and lots of other software). This is not a theoretical concern.


This goes back to what I said about using pointers as implementation details. If you put unique_ptrs in to a _global_ then you're doing something stupid already. Just think about it. Why does something that can only be pointed to from one place need to visible globally in the first place?

My point is all Rust does is force you stop and think, and while C++ lets you do dumb shit, it's hardly fair to blame the language when almost-safe C++ is actually cleaner and easier to read and write than dumb C++.

Build your own handle types with well-defined ownership semantics, use explicit move() sparingly, pass objects by value, use references, utilize the stack and temporaries. Only put pointers inside the guts of classes and your data structure implementations. These techniques go quite far.

And Firefox as an appeal to authority is hardly compelling. As another, I've seen bug fixes in Chromium where the original code quality is so poor it was hard to believe it came out of Google. Of course, since then i've learned most C++ out of Google is total crap.


> Unique pointers provide no protection against use after free, because you can take a reference to their contents and that reference can become dangling.

Yes, it's possible, however references should only ever have local scope so, unless you're dealing with threads or asynchrony it's hard to write sane code where this happens, and if you have those things, and you're passing refs or ptrs, then I don't have to tell you that things are bad.

> as opposed to in C where an explicit call to free is required, this makes C++ more prone to UAF than C.

I don't see this. C++ destructors run after the last line of your code block, if something uses a destructed object then it can only be because you passed a pointer or reference to it to something else that wasn't yet destroyed.

Mentioning free() just implies that you're willing to accept resource leaks to avoid UAF bugs, which is nuts because UAFs can be a lot easier to debug.


> Mentioning free() just implies that you're willing to accept resource leaks to avoid UAF bugs, which is nuts because UAFs can be a lot easier to debug.

If you're focused on security, it goes in the opposite direction: a resource leak can lead to a denial of service, but an use-after-free can lead to remote code execution, which is much worse. From that point of view, it's worth it risking a resource leak if by doing that you prevented a potential instance of remote code execution.

By the way,

> C++ destructors run after the last line of your code block

Aren't there many situations where the C++ destructor runs at the end of the current statement? IIRC, if you call a function which returns a temporary, then call a method on that temporary which returns a reference to within the temporary, and assign the result to a variable, all in a single statement, the temporary will be destructed while the reference to its contents is still live.


> then call a method on that temporary which returns a reference to within the temporary

The obvious answer to this would be never to return references to members (or anything tied to the objects lifefime), but if you really must then you can always use a qualifier to prevent this pattern from compiling.

https://ideone.com/UuZYJe




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: