One problem is that when the stack is allocated it goes into the rest of the same virtual address space, and "growing" it might result in overlapping other address space allocated to the rest of the program. That's why there are guard pages around the stack (also, worth knowing that the stack doesn't grow up on some architectures, it can also grow down like on x86). That's why the post points out that growable stacks have to be moveable.
The main solution is to just reserve a ton of virtual address space but avoid committing to it until the process actually writes to it, which is exactly what OS threads do. They reserve a large amount of virtual address space to start but it's more or less free until a thread actually uses it. However you may not see it released back to the OS until the process exits.
The problem isn't the amount of virtual address space but guaranteeing that when you grow you don't hit anything else. And the solution is to just reserve a ton of it. But then the problem becomes reclaiming memory.
The main solution is to just reserve a ton of virtual address space but avoid committing to it until the process actually writes to it, which is exactly what OS threads do. They reserve a large amount of virtual address space to start but it's more or less free until a thread actually uses it. However you may not see it released back to the OS until the process exits.