0

Let's consider such example:

#include <memory>

int main() {
    int x = 3;
    std::shared_ptr<int> p{&x};
    //std::shared_ptr<int> p = &x;
}

This program has a double-free bug (see also in action at: https://godbolt.org/z/T8eh13). If we were to comment out the line with p{&x} and uncomment the line below it, compilation fails, which is good and which I would expect as per https://stackoverflow.com/a/304169/1923988

The question I have is: could shared_ptr be implemented in a way to also protect us from usages like in the line with p{&x} (in general: usages of taking addresses of automatic variables)?

I imagine the answer is "no, because the callee sees only a pointer type, regardless of whether caller used & or a true pointer variable", but I wonder whether there truly is some fundamental limitation that would prevent compilers from distinguishing such two cases.

socumbersome
  • 243
  • 1
  • 6
  • 2
    The trivial case (as in you can spot it and make a test case) could probably be spotted by a compiler. But the more general case - taking ownership of a raw pointer that's been passed about a few times - is impossible to check and the 1st (obvious) case is probably not worth the effort. `std::make_shared` is the preferred way to create and own a shared object, so taking ownership of a raw pointer is a red flag when doing code inspection(s). – Richard Critten Mar 13 '21 at 11:05
  • 1
    Passing a pointer to an automatic local might even be reasonable in the case where you also provide a custom deleter that does something other than deallocate the pointer. So this would have to be a warning at most. – Useless Mar 13 '21 at 11:14
  • 4
    It's not a double-free bug, but a single-free one. – Evg Mar 13 '21 at 11:28
  • Programmers shouldn't really be taking ownership of random *raw pointers* in modern `C++` code. Raw pointers should be considered *non-owning*, meaning you should assume the object they point to already has an owner, and it isn't you. – Galik Mar 13 '21 at 13:16

1 Answers1

1

Even passing local variables into smart pointers is a valid use-case.

Suppose you are using a C-style OOP API:

struct A { /* ... */ };
void init_a(A* a);
void destroy_a(A* a);

Then:

int main(int argc, char** argv) {
    A a;
    init_a(&a);
    std::unique_ptr<A, void(*)(A*)> a_raii(&a, destroy_a);
}

In theory the compiler should be able to detect in some scenarios that you are constructing a stdlib smart pointer using a pointer to a local variable without a custom deleter, but that would require quite some special code to detect one very specific bug in the giant landscape of C++ bugs.

orlp
  • 112,504
  • 36
  • 218
  • 315
  • > Even passing local variables into smart pointers is a valid use-case. Wouldn't that invalidate the answer at https://stackoverflow.com/a/304169/1923988 ? It might not be clear from how I worded my question, but a part of it also touches on: why would one way of constructing give protection while the other not? It looks inconsistent and given also https://stackoverflow.com/questions/66612661/can-smart-pointer-be-safer-by-disallowing-automatic-variables?noredirect=1#comment117757100_66612661 I'd say it should rather be named `shared_unsafe_ptr` :) – socumbersome Mar 13 '21 at 14:44