Если я правильно понимаю, при переполнении буфера слишком длинные данные затирают в стеке адреса возврата. Это позволяет подставить и выполнить свой код.
Но почему-бы не разделить стек? Сделать отдельный стек для данных и отдельный стек для адресов возврата.
Простейший способ — организовать где-то в памяти стек и использовать его для хранения локальных переменных и параметров процедур. А адрес возврата хранить в "обычном" стеке.
Это же вроде компилятор решает, где и как хранить данные? Есть такая настройка?