Yes, this is a real problem. Your program's behavior is undefined. c is a different object from the pointer referenced by b, and its lifetime ends at the end of doWarning. Those two pointers point to the same A object (d), but that doesn't mean they're the same object.
To illustrate, I'll go more-or-less line-by-line and use diagrams:
A d;
auto m = doWarning(&d);
This creates an A object named d and passes an anonymous pointer to that object to doWarning. I'll get to m later, but for now the objects in play look like this:
d
┌─────┐ ┌─────┐
│ │ │ │
│ A* ├──────►│ A │
│ │ │ │
└─────┘ └─────┘
template<typename T>
T const& doWarning(T const& b)
{
Here, T will be deduced to be A*, since that's what got passed to it.
doWarning accepts its parameter by reference, so the type of b will be A* const &. That is, b is a reference to the anonymous pointer to d from main:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
A* c = returningLocalPointer(b);
Here you create another pointer, c, that points to the same object as b. I won't look at returningLocalPointer, since it's more-or-less irrelevant. This line could be replaced with A* c = b; and nothing would change. Your objects now look like this:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
▲
c │
┌─────┐ │
│ │ │
│ A* ├──────────┘
│ │
└─────┘
As you can see, c is a different object than the object referenced by b.
return c;
Since doWarning returns an A* const& (since T is A*), this initializes the return value to refer to the local variable c:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
▲
return value c │
┌───────────┐ ┌─────┐ │
│ │ │ │ │
│ A* const& ├──────►│ A* ├──────────┘
│ │ │ │
└───────────┘ └─────┘
}
Now doWarning ends, and so its local variable c goes out of scope and its lifetime ends. That leaves doWarning's return value dangling:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
return value
┌───────────┐
│ │
│ A* const& ├──────► Nothing here anymore
│ │
└───────────┘
auto m = doWarning(&d);
Now we get back to m. auto by itself will never deduce a reference type, so the type of m is deduced to be A*. That means the program will attempt to copy the pointer referenced by the reference that doWarning returned. The pointer that doWarning's return value referenced no longer exists though. Trying to copy a nonexistent object is an error, and if a program does so its behavior is undefined.