It's undefined behavior, despite not calling free anywhere. Replace with std::string and it could be use after free. In C, UAF wouldn't happen that silently for heap memory.
From my POV compilers should warn that this code is potentially dangerous. My rule is: never return a reference or pointer from a function to something whose lifetime you don't have control of.
With C++, good luck even reading and understanding The ISO C++ Standard. Not to say fully internalizing all possible interactions between all nuances. I presume not all of them may even be truly 100% fully fleshed out and explored in The Book. As to writing a compiler - even just writing a parser for C++ is famously super hard!
Anyone that is able to keep in mind the documented 200 cases of UB is my hero.
Anybody who has the faintest clue about both languages should know that.