Domanda

Esistono alcune condizioni che possono causare overflow dello stack su un sistema Linux x86:

  • struct my_big_object [HUGE_NUMBER] nello stack. Percorrerlo alla fine provoca SIGSEGV .
  • La routine alloca () (come malloc () , ma usa lo stack, si libera automaticamente e esplode anche con SIGSEGV se è troppo grande). Aggiornamento: alloca () non è formalmente deprecato come ho affermato in origine; è semplicemente scoraggiato .

Esiste un modo per rilevare a livello di codice se lo stack locale è abbastanza grande per un determinato oggetto? So che la dimensione dello stack è regolabile tramite ulimit , quindi spero che ci sia un modo (per quanto non portatile possa essere). Idealmente, vorrei poter fare qualcosa del genere:

int min_stack_space_available = /* ??? */;
if (object_size < min_stack_space_available)
{
    char *foo = alloca(object_size);
    do_stuff(foo);
}
else
{
    char *foo = malloc(object_size);
    do_stuff(foo);
    free(foo);
}
È stato utile?

Soluzione

È possibile determinare lo spazio di stack disponibile per il processo trovando la dimensione dello spazio di stack di un processo e quindi sottraendo l'importo utilizzato.

ulimit -s

mostra le dimensioni dello stack su un sistema linux. Per un approccio programmatico, consulta getrlimit () . Quindi, per determinare la profondità corrente dello stack, sottrarre un puntatore all'inizio dello stack da uno a quello inferiore. Ad esempio (codice non testato):

unsigned char *bottom_of_stack_ptr;

void call_function(int argc, char *argv) {
    unsigned char top_of_stack;
    unsigned int depth = (&top_of_stack > bottom_of_stack_ptr) ? 
        &top_of_stack-bottom_of_stack_ptr : 
        bottom_of_stack_ptr-&top_of_stack;

    if( depth+100 < PROGRAMMATICALLY_DETERMINED_STACK_SIZE ) {
        ...
    }
}

int main(int argc, char *argv) {
    unsigned char bottom_of_stack;
    bottom_of_stack_ptr = &bottom_of_stack;
    my_function();
    return 0;
}

Altri suggerimenti

  

La routine alloca () deprecata (come malloc (), ma usa lo stack,   si libera automaticamente e esplode anche con SIGSEGV se è troppo grande).

Perché alloca è deprecato?

Comunque, quanto più velocemente nel tuo caso è alloca vs malloc? (Ne vale la pena?)

E non ricevi nulla indietro da alloca se non c'è abbastanza spazio? (allo stesso modo di malloc?)

E quando il tuo codice si blocca, dove si blocca? è in alloca o è in doStuff ()?

/ Johan

Non sono sicuro che ciò si applichi su Linux, ma su Windows è possibile imbattersi in violazioni dell'accesso con allocazioni di stack di grandi dimensioni anche se hanno esito positivo!

Questo perché, per impostazione predefinita, VMM di Windows in realtà segna solo le prime (non sono sicure quante esattamente) pagine da 4096 byte della RAM dello stack come paginabili (ovvero supportate dal file di paging), poiché ritiene che gli accessi allo stack generalmente marcia verso il basso dall'alto; man mano che gli accessi si avvicinano sempre più all'attuale "limite", le pagine inferiore e inferiore sono contrassegnate come impaginabili. Ciò significa che una lettura / scrittura della memoria iniziale molto al di sotto della parte superiore dello stack attiverà una violazione di accesso poiché tale memoria non è stata ancora allocata!

alloca () restituirà NULL in caso di errore, credo che il comportamento di alloca (0) sia indefinito e variante di piattaforma. Se lo controlli prima di do_something (), non dovresti mai essere colpito con un SEGV.

Ho un paio di domande:

  1. Perché, oh perché, hai bisogno di qualcosa di così grande in pila? La dimensione predefinita sulla maggior parte dei sistemi è 8M, che è ancora troppo piccola?
  2. Se la funzione che chiama alloca () si blocca, proteggerebbe la stessa quantità di heap tramite mlock () / mlockall () garantirebbe quasi le stesse prestazioni di accesso (es. " Non scambiarmi, fratello! ") tempo? Se stai utilizzando uno scheduler 'rt' più aggressivo, ti consigliamo di chiamarli comunque.

La domanda è interessante ma solleva un sopracciglio. Solleva l'ago sul mio quadrato-piolo-tondo-o-metro.

Non dici molto sul perché desideri allocare nello stack, ma se è attraente il modello di memoria dello stack, puoi implementare anche l'allocazione dello stack sull'heap. Allocare una grande porzione di memoria all'inizio del programma e mantenere uno stack di puntatori a questo che corrisponderebbe ai frame sullo stack normale. Devi solo ricordare di far apparire il puntatore dello stack privato quando la funzione ritorna.

Diversi compilatori, ad esempio Open Watcom C / C ++ , supportano la funzione stackavail () che ti permette di fare esattamente questo

Puoi usare GNU libsigsegv per gestire un errore di pagina, compresi i casi in cui si verifica un overflow dello stack (dal suo sito Web):

  

In alcune applicazioni, il gestore di overflow dello stack esegue alcune operazioni di pulizia o notifica all'utente e quindi termina immediatamente l'applicazione. In altre applicazioni, il gestore di overflow dello stack esegue il longjmps su un punto centrale dell'applicazione. Questa libreria supporta entrambi gli usi. Nel secondo caso, il gestore deve assicurarsi di ripristinare la normale maschera di segnale (poiché molti segnali vengono bloccati mentre il gestore viene eseguito) e deve anche chiamare sigsegv_leave_handler () per trasferire il controllo; solo allora può allontanarsi.

La funzione alloca è non obsoleta. Tuttavia, non è in POSIX ed è anche dipendente dalla macchina e dal compilatore. La man-page di Linux per alloca fa notare che "per alcune applicazioni, il suo uso può migliorare l'efficienza rispetto all'uso di malloc e in alcuni casi può anche semplificare la deallocazione della memoria in applicazioni che usano longjmp () o siglongjmp (). Altrimenti, il suo uso è scoraggiato. & Quot;

La manpage dice anche che "non vi è alcuna indicazione di errore se il frame dello stack non può essere esteso. Tuttavia, dopo un'assegnazione non riuscita, è probabile che il programma riceva un SIGSEGV. & Quot;

Le prestazioni di malloc sono state effettivamente menzionate nel Stackover Podcast # 36 .

(So che questa non è una risposta adeguata alla tua domanda, ma ho pensato che potesse essere utile comunque.)

Anche se questa non è una risposta diretta alla tua domanda, spero che tu sia a conoscenza dell'esistenza di valgrind - uno strumento meraviglioso per rilevare tali problemi in runtime, su Linux.

Per quanto riguarda il problema dello stack, è possibile tentare di allocare gli oggetti in modo dinamico da un pool fisso che rileva questi overflow. Con una semplice macro-procedura guidata puoi eseguire questa operazione al momento del debug, con il codice reale in esecuzione al momento del rilascio e quindi sapere (almeno per gli scenari che stai eseguendo) che non stai prendendo troppo. Altre informazioni e un link a un'implementazione di esempio.

Non c'è un bel modo a cui possa pensare. Forse è possibile usando getrlimit () (suggerito prima) e qualche aritmetica del puntatore? Ma prima chiediti se lo vuoi davvero.

void *closeToBase;

main () {
  int closeToBase;
  stackTop = &closeToBase;
}

int stackHasRoomFor(int bytes) {
  int currentTop;
  return getrlimit(...) - (¤tTop  - closeToBase) > bytes + SomeExtra;
}

Personalmente, non lo farei. Allocare grandi cose sullo heap, lo stack non era pensato per questo.

La fine dell'area dello stack è determinata dinamicamente dal sistema operativo. Sebbene sia possibile trovare " statico " limiti dello stack osservando le aree di memoria virtuale (VMA) in modo fortemente dipendente dal sistema operativo (vedere i file stackvma * in libsigsegv / src / ), dovrai inoltre prendere in considerazione

Ci scusiamo se questo sta affermando l'ovvio, ma potresti facilmente scrivere una funzione per testare una specifica dimensione di allocazione dello stack semplicemente provando l'alloca (di quella dimensione) e rilevando un'eccezione di overflow dello stack. Se lo si desidera, è possibile inserirlo in una funzione, con alcuni calcoli predeterminati per il sovraccarico dello stack di funzioni. Ad esempio:

bool CanFitOnStack( size_t num_bytes )
{
    int stack_offset_for_function = 4; // <- Determine this
    try
    {
        alloca( num_bytes - stack_offset_for_function );
    }
    catch ( ... )
    {
        return false;
    }

    return true;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top