There's what's called "return-oriented programming" (AKA ROP) type of exploits.
The attacker finds how to make his evil code out of different pieces of the program that's being exploited.
He finds usable byte sequences (instructions) before the return instruction byte(s) that can do useful operations on registers or memory like move a value to a location, add values, compare values, etc etc. Those are micro subroutines that the exploit gets built of.
Then by exploiting a code bug the attacker forces the program to start executing the chain of those micro subroutines that does all the evil work.
So, now good code turns into evil code. Nothing's executed on the stack or in the heap.
It's also noteworthy that on CPUs where instructions span multiple bytes and are of variable length, even the immediate instruction operands (IOW, numerical constants) that are part of instructions can become code, and so the chances of finding usable byte sequences are higher there than on "simpler" CPUs.
It's also often possible to construct malicious code that will change memory protection and the exploit will no longer be constrained by the existing application's code.