Il problema principale nel far crescere lo stack è che deve sedersi all'inizio della memoria e avere un limite di dimensione superiore forzato, oppure sedersi nella parte superiore della memoria e avere una posizione forzata. Entrambe queste situazioni limitano l'utilizzo massimo della memoria. Se sia la pila che l'heap crescono l'una verso l'altra da estremità opposte, si esaurisce la memoria solo quando non ne rimane letteralmente nessuna. In altre configurazioni potresti esaurire lo stack o l'heap pur avendo memoria gratuita. 
 L'idea di "stack indietro" è stata lanciata da un po 'di tempo e generalmente non è vista come una soluzione solida. Per cominciare, ti imbatterai in problemi se stai modificando i buffer dello stack che si trovano nei frame precedenti. Inizialmente questo non sembra essere comune, ma considera di allocare i buffer per molte funzioni  prima  che le passi: 
void foo(char* src)
{
    char dest[20];     // local allocated within frame of foo
    strcpy(dest, src); // new stack frame, so return ptr AFTER dest
    // blah blah rest of code...
    if (dest[0] == 'X')
        printf("First char is X\n");
}
 Si noti che il frame dello stack per   strcpy    si posizionerà numericamente sopra il frame dello stack per   foo   . In un sistema tipico,   strcpy    scriverà oltre il buffer, sopra il puntatore di ritorno di   foo   , portando al controllo del puntatore di istruzioni dopo che   foo    ritorna. Con uno stack all'indietro,   strcpy    scriverà, attraverso la fine dello stack frame di   foo   , nella parte inferiore dello stack frame di   strcpy   , portando a una sovrascrittura dell'indirizzo di ritorno di   strcpy   , e di nuovo dando il controllo del puntatore di istruzioni. 
 Una soluzione considerata da molti abbastanza sicura consiste nell'utilizzare un'architettura con uno stack per locali e parametri (lo stack di dati) e un'altra per i frame dello stack e i puntatori di ritorno (lo stack di controllo). Questo isolamento garantisce che i puntatori di ritorno non possano essere sovrascritti casualmente quando un buffer locale viene sovraccaricato. Ha di nuovo problemi con la gestione della memoria, anche se solo uno stack deve avere una dimensione massima fissa - l'altro può crescere nell'heap. 
 Naturalmente, anche questa architettura non è completamente robusta dal punto di vista della sicurezza. Considera quanto segue: 
int int_sorter( const void *val_a, const void *val_b )
{
    // this code isn't important here
    int first = *(int*)val_a;
    int second = *(int*)val_b;
    if ( first == second )
        return 0;
    else return (first < second) ? -1 : 1;
}
void bar(char* message, int (*sorter)(const void*,const void*))
{
    int array[10];
    char dest[32];
    // do something with array
    // ...
    strcpy(dest, message);
    qsort(array, 10, sizeof( int ), sorter);
}
 In questo caso, supponendo una coppia di classiche pile in crescita in un'architettura a doppio stack,   message    sovrasta   dest    sullo stack di dati, copiando il puntatore di funzione   sorter    che è stato premuto come secondo parametro a   bar   . La chiamata a   strcpy    ritorna normalmente (lo stack di controllo è intatto), ma la chiamata a   qsort    contiene un'istruzione per passare alla funzione   sorter   , che di nuovo porta al controllo sul puntatore di istruzioni. 
 Alla fine della giornata, non troverai una soluzione completa al problema. Il meglio che puoi fare è usare buone pratiche di codifica (o insegnare ai tuoi sviluppatori di farlo) e abilitare protezioni come ASLR (a.k.a. PIE), DEP / NX, stack di canarini, ecc.