2016-03-24 13 views
0

Ich verstehe, dass in C, Arrays, die eine Länge bei der Deklaration gegeben werden können. Ich möchte wissen, ob diese Längenangaben einfach für andere Programmierer sind, um die Verwendung von zu sehen und zu verstehen, oder ob der Compiler dazu gebracht werden kann, den Code zu schützen, indem er das Lesen von mehr als der Pufferlänge von Zeichen verbietet. Wenn ich eine Zeichenfolge einlese, wird sie einfach weiter ausgeführt und beginnt Daten zu überschreiben, die in Variablen gespeichert sind, die nach dem Puffer deklariert sind, in den ich einlesen möchte. Gibt es sichere Möglichkeiten, Daten einzulesen?Wie behandelt C Pufferüberläufe?

char arr[5];                 
char buff[5] = "cat";                                   
printf("The buffer holds: %s\n", buff);          
printf("Input a word to be held in \"arr\": ");        

scanf("%s", arr);               

printf("The array holds: %s\n", arr);          
printf("The buffer holds: %s\n", buff);          
printf("%c\n", arr[9]);  

Wenn die Zeichenfolge in arr lesen lang genug ist, „cat“ überschrieben ist, und keines der Kompilierung Flaggen scheinen etwas zu tun (ich kompilieren mit -Wextra -Wall Werror -std = c99) Der einzige Was sich beklagt, ist Valgrind. Wie schreibe ich sicheren Array-Code in C?

+1

Sie handeln diszipliniert. Der "C" -Standard gibt an, dass Compiler-Anbieter nicht verpflichtet sind, Sie zu verteidigen. Außerhalb der Grenzen schreiben ist ein undefiniertes Verhalten. – StoryTeller

+0

haben Sie versucht, '-O2' hinzuzufügen? –

+1

lm gt fy ... 'safe scanf' –

Antwort

3

In gewissem Sinne schützt die C-Sprache weder Sie selbst noch schützt Sie davor, über die Grenzen eines Arrays hinauszugehen. Genauer gesagt, ein C-Compiler muss keine Grenzen prüfen, aber es ist erlaubt, dies zu tun. (Nur wenige Compiler nutzen diese Berechtigung Sehr wenige tun dies standardmäßig..)

Zum Beispiel, wenn Sie schreiben:

int arr[10]; 
arr[20] = 42; 

das Verhalten ist nicht definiert . Das bedeutet nicht, dass Ihr Programm abstürzt. Dies bedeutet nicht, dass der Fehler oder nicht erkannt wird. Es ist den ISO-C-Standard zu zitieren,

Verhalten bei der Verwendung eines nonportable oder fehlerhaften Programmkonstrukts oder von fehlerhaften Daten, für die diese Internationale Norm keine Anforderungen

vergrößern A typisch erlegt C-Compiler wird wahrscheinlich Code generieren, der die Basisadresse arr übernimmt, einen Offset von 20 * sizeof (int) hinzufügt und anschließend versucht, 42 am resultierenden Speicherort zu speichern. Ohne explizite oder implizite Überprüfungen könnte dies einige andere Datenstrukturen überlisten, es könnte in den Speicher schreiben, der Ihrem Prozess gehört, aber für nichts anderes verwendet wird, oder Ihr Programm könnte beendet werden. (Oder #include <stdjoke.h> könnte es machen Dämonen aus der Nase fliegen.)

Aber ein konformer C-Compiler könnte Code hinzufügen, um zu überprüfen, dass der Index im Bereich von 0 bis 9, und nehmen Sie sich etwas vernünftige Aktion, wenn es isn‘ t. C verbietet nicht, Grenzen zu prüfen; es erfordert es einfach nicht.

Und in diesem speziellen Fall ist es möglich (aber nicht erforderlich), zur Kompilierzeit zu erkennen, dass der Array-Zugriff außerhalb der Grenzen liegt, sodass ein Compiler eine Kompilierungswarnung ausgeben kann. (Dies ist nicht möglich, wenn der Indexwert erst zur Laufzeit bekannt ist.)

Letztendlich liegt die Verantwortung für die Vermeidung von Out-of-Bounds-Zugriffen bei Ihnen, dem Programmierer. Gehen Sie nicht davon aus, dass der Compiler das für Sie überprüft - und gehen Sie nicht davon aus, dass dies nicht der Fall ist.

0

C schützt Sie nicht davor, über das Ende eines Arrays hinauszugehen. Es gibt jedoch Möglichkeiten, dies zu erkennen. Sehen Sie diesen Beitrag

Setting up a bounds-protected array

Versuchen Sie diesen Code

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define ARRAY_SIZE 100 

int main(void) { 
    size_t i = 0; 
    char arr1[ARRAY_SIZE]; 
    char * arr2 = malloc(ARRAY_SIZE); 
    for(i = 0; i < 200; i++) { 
    arr1[i] = '1'; 
    arr2[i] = '2'; 
    } 

    for(i = 0; i < 200; i++) { 
    printf("%zu arr1[i]=%c \n", i, arr1[i]); 
    printf("%zu arr2[i]=%c \n", i, arr2[i]); 
    } 
    return 0; 
} 

folgende Kompilierung Optionen (Dies funktioniert nur mit gcc dh Klirren keine Fehler geben)

gcc -O3 -Wall -std=c11 -pedantic array_overflow_at_03.c 

dann versuchen, es unter Verwendung

gcc -Wall -std=c11 -pedantic array_overflow_at_03.c 

Jede Methode, um dies zu tun, hat ihre Vorzüge, Ihre Anwendungsanforderungen würden bestimmen, welche zu verwenden.

+1

Danke für Ihre Hilfe, Harry. Das Problem besteht darin, dass dieses Verfahren post-fact ist, d. H. Nachdem der nicht autorisierte Zugriff stattgefunden hat. Nur die Verwendung des Speicherverwaltungssystems (Hardware) kann währenddessen schützen. Aber wie können Sie dann jede Variable schützen? Sie können wahrscheinlich nicht so alle illegalen Zugriffe überprüfen. Und wann ist ein Zugang illegal? –

+0

Ha, schöne! 'i' wird überschrieben in' arr1 [i] = '1'; 'wenn i == 101, setzt 'i' auf 49 (' 1 ') zurück und die Schleife läuft für immer. –

1

C folgt der Philosophie „der Programmierer weiß es am besten“ und „I hält Sie nicht die Hand“

Aus diesem Grund C so schnell ist, es keine Kontrollen zu tun hat.

fgets(arr, sizeof(arr), stdin); 

arr halten die Eingabe bis zur angegebenen Größe:

Für eine sichere Benutzereingabe können Sie fgets

etwas entlang der Linien von verwenden. Für weitere Informationen empfehle ich die man-Seite für fgets http://linux.die.net/man/3/fgets

Sie müssen möglicherweise mehrere Aufrufe von diesem, um alle Eingaben von Stdin zu bekommen.

+0

Danke für die alternative Methode! Es neigt dazu, solche Dinge zu sein, die mich dazu bringen wollen, Benutzern nur Schaltflächen zum Anklicken zu bieten, um die Fehlerprüfung zu minimieren ... Oder stattdessen C++ für Dinge wie Strings und Stringstreams verwenden. – guptashark

0

Eine Array-Größe in C teilt dem Compiler nur mit, wieviel Speicher für das Array reserviert werden muss. C fügt keinen Code ein, um zu prüfen, ob Sie über die Array-Grenze hinausgehen. die Größe '5' in int a[5]; ist nirgendwo im kompilierten Programm gespeichert. Es ist nur im Quellcode. Andere Programmierer, die den Quellcode sehen können, können es sehen; niemand anderes kann.

Da C nicht überprüft, was Sie tun und Ihre Hand halten (siehe Lyle Rolleman's Antwort), wird C einen Pufferüberlauf nicht "erkennen". Folglich ist das Verhalten undefiniert, wenn dies geschieht (sogenanntes "Undefined Behavior" oder UB). Was häufig passiert ist, dass der Stapel überschrieben wird, und auf dem Stapel ist die Rücksprungadresse zum Anrufer. Dies wird überschrieben, wenn die aktuelle Funktion zurückkehren möchte, springt sie auf "Nirgendwo" (oder irgendwo, da dieses Verhalten von "Stack-Exploits" von Hackern verwendet wird, die den Stack sorgfältig überschreiben, so dass der Sprung zu "ihr-wo" ist) .