I've been reading more about security lately, and one thing I don't understand is how buffer overflows allow for true RCE these days. I believe all modern processors support having non-executable pages; why is any program-writeable page executable? Especially in C, where there's no JIT. It also seems like address space randomization would make RCE pretty hard.
Obviously they can still arbitrarily write the instruction pointer, which is very bad and possibly bootstrappable into fully RCE, but I still see more conventional RCE described in recent books and wonder if and why it's still doable.
You should distinguish RCE and the ability to mount working, reliable exploits. RCE is possible here, and its best to consider that a fatal security failing in and of itself rather than hoping a particular set of countermeasures will be completely effective.
NX pages would make some classes of RCE exploit difficult or hard (such as including shellcode directly in the POP3 response, and returning directly to it) but doesn't help for many other classes (return-to-libc, or finding or installing 'gadgets' elsewhere in executable pages).
ASLR is a working countermeasure for other classes of exploit, as long as addresses are unpredictable enough to make a search is hard.
You can redirect the control-flow of the program by overwriting a return address or a vtable. Once you've done that, it's easiest if you can redirect control to code you've written, in executable heap; but if that's not possible, you can still use return-to-libc, or potentially "return-oriented programming" strategies.