Controllo della dimensione dell'array in C / C ++ per evitare errori di segmentazione

5

Quindi è risaputo che C non ha limiti di array durante l'accesso alla memoria. Al giorno d'oggi, se chiami myArray[7] quando lo hai inizializzato come int myArray[3] , il tuo programma otterrà un segfault e un arresto anomalo grazie alla memoria protetta.

Ora, se hai un argomento in una funzione come myFunc(int *yourArray) , ma sai che hai bisogno di almeno 8 slot nell'array, è possibile controllare se myArray[7] è illegale in anticipo per lanciare una custom errore:

"Sorry, yourArray is too small for this function. We need 8 ints of space."

anziché

"Segmentation fault."
    
posta CJxD 19.08.2015 - 22:05
fonte

5 risposte

9

Il controllo dei limiti di array come desideri è specifico per l'implementazione, perché overflow del buffer è un esempio di comportamento non definito (e questo spiega perché UB può essere davvero pessimo)

È anche un problema indecidibile in generale. Puoi facilmente mostrare che staticamente trovare (tramite analisi del programma statico , ad es. Del codice sorgente C ++ , senza eseguire effettivamente il programma) ogni overflow del buffer equivale al problema di interruzione . Leggi anche su Teorema di Rice .

Tuttavia, esistono diversi strumenti (parziali) pratici (in particolare su Linux):

  • potresti aggiungere assert o static_assert -s nel codice e / o i controlli di runtime.

  • potresti trovare e utilizzare un analizzatore di codice statico alla Frama-C (attualmente funziona con il codice C).

  • potresti personalizzare il tuo compilatore GCC usando MELT .

  • Dovresti compilare il tuo codice con tutti gli avvertimenti e amp; informazioni di debug, ad es. g++ -Wall -Wextra -g se si utilizza GCC .

  • Potresti eseguire il tuo programma con valgrind , almeno per i test.

  • potresti utilizzare lo disinfettante degli indirizzi , ad es. aggiungi -fsanitize=address ai tuoi flag di compilazione (durante il test)

  • in particolare in C (e talvolta in C ++) è una buona convenzione per passare sia i puntatori agli array che le loro dimensioni (come ad esempio snprintf(3) o strncmp (3) do). In C puoi anche utilizzare membri di array flessibili in struct e archiviare le dimensioni della matrice flessibile all'interno di struct

Le abilità aritmetiche del puntatore CW e C ++ rendono ancora più difficile trovare buffer overflow.

In C ++ 11 è meglio evitare array semplici e puntatori grezzi e utilizzare contenitori standard e puntatori intelligenti .

    
risposta data 19.08.2015 - 22:11
fonte
4

La risposta è davvero abbastanza semplice: se vuoi sicurezza, usa qualcosa che in realtà la fornisce - e non è C, e non grezzi array in stile C.

Senza allontanarti troppo dallo stile di base di C e dalle matrici non elaborate, puoi usare C ++ e un std::vector con [i] sostituito da .at(i) e ottenere il controllo dei limiti.

L'utilizzo di std::vector invece semplifica la maggior parte dei problemi con gli array. Puoi controllare la dimensione corrente del vettore con la sua funzione membro .size() . Il più delle volte, non è necessario farlo, perché quando vuoi aggiungervi qualcosa basta usare la sua funzione membro .push_back() .

Almeno in teoria, puoi fare lo stesso tipo di cose in C, ma farlo diventa relativamente brutto. Sebbene non sia terribilmente difficile definire un wrapper che (per esempio) metta un puntatore e una dimensione di allocazione corrente in un struct , devi definire le funzioni per fare tutta la manipolazione su di esso, e anche allora devi convivere con Infatti il codice esistente non saprà come usarlo o gestirlo. L'ho fatto un paio di volte, e se ne hai abbastanza bisogno puoi farlo funzionare - ma molto tempo fa ho deciso che non valeva la pena.

    
risposta data 20.08.2015 - 01:21
fonte
4

Una funzione che riceve un puntatore non sa della lunghezza dell'array corrispondente. Devi passare esplicitamente come parametro:

void myFunc(int *yourArray, size_t yourArrayLen)

Dopo averlo fatto, lanciare un errore è banale.

Naturalmente, questo lascia comunque la possibilità che il tuo chiamante ti dia la lunghezza sbagliata. Non puoi davvero evitarlo senza:

  • implementazione di un tipo di dati personalizzato per archiviare gli array e quindi assicurarsi che la lunghezza rimanga sincronizzata con la lunghezza reale in ogni momento usando l'incapsulamento, o
  • che consente solo array statici, ad es.

    void myFunc(int (*yourArray)[8]);
    
risposta data 20.08.2015 - 01:46
fonte
1

Non c'è modo in C (++) di ottenere la lunghezza di una matrice da un puntatore al suo primo elemento. (Esistono funzioni specifiche della piattaforma come _msize in MSVCRT, ma solo funziona su malloc ed i puntatori.)

Ciò che viene in genere fatto quando si passano le matrici alle funzioni è di passare la lunghezza insieme al puntatore in modo che il controllo dei limiti possa essere eseguito in fase di runtime .

void myFunc(int* yourArray, int length)
{
    if (length < 8)
    {
        puts("Sorry, yourArray is too small for this function. We need 8 ints of space.");
        return;
    }

    // ...
}

void caller()
{
    int arr[LEN];
    myFunc(arr, LEN);
}
    
risposta data 19.08.2015 - 22:58
fonte
0

Usa un wrapper personalizzato per malloc (o scrivi il tuo) che mantiene ulteriori informazioni sui blocchi che assegna. Quello che uso aggiunge alcuni "byte di guardia" a ogni allocazione, incorpora la lunghezza dell'allocazione come a [-1] e controlla i byte di guardia e altre cose sulla deallocazione.

    
risposta data 19.08.2015 - 23:39
fonte

Leggi altre domande sui tag