*Markus: (C) sichere Eingabe? (Buffer Overflows vermeiden)

Hallo,

irgendwie verstehe ich nicht, wie man in C eine wirklich deppensichere Eingabe realisieren soll, damit Buffer Overflows vermieden werden.
Dabei tüftelte ich mit fgets herum, aber fgets hat die dumme Angewohnheit, dass es alle Strings, als die die länger als die angegebene sind, im Tastaturbuffer speichert, und beim nächsten Mal hinten dranhängt.
sscanf ist überhaupt ein witziges Beispiel. Hierbei kann man zwar die Länge der Eingabe beschränken, muss aber einen "buffer" angeben. Anders ausgedrückt:
Ich muss zuerst einen String irgendwie in einen Buffer einlesen, um dann überhaupt mal überprüfen zu können, ob der String überhaupt von der Länge passt, wobei es dann natürlich schon zu spät ist, da ein Buffer Overflow bereits stattgefunden hätte können.
Also, wie löst man dieses Problem am elegantesten?

Markus.

--
http://www.apostrophitis.at
STANDAR_D_  - ist das wirklich so schwer?
  1. &Markus,

    Hierbei kann man zwar die Länge der Eingabe beschränken, muss aber einen "buffer" angeben. Anders ausgedrückt:
    Ich muss zuerst einen String irgendwie in einen Buffer einlesen, um dann überhaupt mal überprüfen zu können, ob der String überhaupt von der Länge passt, wobei es dann natürlich schon zu spät ist, da ein Buffer Overflow bereits stattgefunden hätte können.

    Meines Wissens bricht Scanf nach der angegebenen Anzahl von zeichen ab, schreibt also nicht einfach den Buffer weiter voll.
    Das ist nämlich (wenn ich mich richtig erinnere) gerade der Witz dabei, weswegen man eine Länge angeben kann, damit es eben genau nicht zum Buffer overflow kommen kann.

    Gruesse,
    Jorg

    1. Hallo,

      Meines Wissens bricht Scanf nach der angegebenen Anzahl von zeichen ab, schreibt also nicht einfach den Buffer weiter voll.

      So weit ich das verstanden habe, liest sscanf aus dem "Buffer" aber aus, um die Daten dann weiterverarbeiten zu können:

      int  sscanf ( char * buffer, const char * format [ , argument , ...] );

      /* buffer  Buffer containing the string to be parsed for data. */

      Das ist das, was ich meinte. Wo soll ich "buffer" herbekommen, wenn ich nicht schon vorher etwas irgendwie eingelesen habe.

      Markus.

      --
      http://www.apostrophitis.at
      STANDAR_D_  - ist das wirklich so schwer?
      1. Moin!

        Meines Wissens bricht Scanf nach der angegebenen Anzahl von zeichen ab, schreibt also nicht einfach den Buffer weiter voll.

        So weit ich das verstanden habe, liest sscanf aus dem "Buffer" aber aus, um die Daten dann weiterverarbeiten zu können:

        int  sscanf ( char * buffer, const char * format [ , argument , ...] );

        /* buffer  Buffer containing the string to be parsed for data. */

        
        >   
        > Das ist das, was ich meinte. Wo soll ich "buffer" herbekommen, wenn ich nicht schon vorher etwas irgendwie eingelesen habe.  
          
        Es gibt doch auch noch fscanf: `int fscanf(FILE * restrict stream, const char * restrict format, ...);`{:.language-c}  
          
        Ansonsten möchtest du vielleicht C++ probieren: Wenn du die string-Klasse im Zusammenhang mit Streams benutzt, hast du eine recht sichere Eingabe, z.B. Einlesen einer Zeile:  
          
        ~~~c
          
        #include <string>  
        #include <iostream>  
          
        std::string eingabe;  
          
        std::getline(std::cin, eingabe);    // eingabe enthält nun genau eine Zeile  
        std::cin >> eingabe;                // eingabe enthält ein Wort  
        
        

        Viele Grüße,
        Robert

        1. Hallo,

          mit fscanf funktioniert es zwar, aber es tritt genau dieses lästige Verhalten wie bei fgets auf, nämlich dass die Zeichen die zuviel sind, einfach solange weiter ausgegeben werden, bis der String zu Ende ist.

          #include <string>
          #include <iostream>

          std::string eingabe;

          std::getline(std::cin, eingabe);    // eingabe enthält nun genau eine Zeile
          std::cin >> eingabe;                // eingabe enthält ein Wort

            
          Und was passiert, wenn die Zeile 1000 Zeichen lang ist? :)  
            
          Markus.  
          
          -- 
          <http://www.apostrophitis.at>  
            
            
          STANDAR\_D\_  - ist das wirklich so schwer?  
            
          ![](http://signatur.pithax.net/gentoo.jpg)
          
          1. Moin!

            mit fscanf funktioniert es zwar, aber es tritt genau dieses lästige Verhalten wie bei fgets auf, nämlich dass die Zeichen die zuviel sind, einfach solange weiter ausgegeben werden, bis der String zu Ende ist.

            Moment, heißt das, dass die übrigen Zeichen im Eingabepuffer verbleiben, bis der nächste Lesevorgang sie abholt oder liest du ständig das gleiche? Letzteres könnte an einem nicht abgeholten Newline liegen.

            #include <string>
            #include <iostream>

            std::string eingabe;

            std::getline(std::cin, eingabe);    // eingabe enthält nun genau eine Zeile
            std::cin >> eingabe;                // eingabe enthält ein Wort

            
            >   
            > Und was passiert, wenn die Zeile 1000 Zeichen lang ist? :)  
              
            Das ganze fällt dir unter Umständen dann auf den Fuß, wenn die Eingabe größer `pow(2, sizeof(size_t)*8))`{:.language-c} (entspricht mathematisch [latex]2^{sizeof(size\\_t) \* 8}[/latex]) ist (unter der Annahme, dass auf deinem Rechner ein Byte aus 8 Bit besteht ;-) ) und der komplette virtuelle Speicher dem Programm zur Verfügung steht.  
              
            Viele Grüße,  
            Robert
            
            1. Hallo,

              ich glaube, dass ich jetzt eine Möglichkeit gefunden habe, die man nicht austricksen kann:

                
              #include <stdio.h>  
                
              int main(void)  {  
              int c;  
              int i = 0;  
              char str[11];  
                
                  while( ((c = getchar()) != '\n') )   {  
                       if (i < 10)   {  
                       str[i] = c;  
                       }  
                 i++;  
                 }  
               str[10] = '\0';  
                
              printf("Output: %s\n", str);  
                
              return 0;  
              }  
              
              

              Gäbe es hier noch etwas zu bemängeln? Ich habe es versucht, auf die übelste Weise zu vergewaltigen, aebr es scheint sicher zu sein :)

              Markus.

              --
              http://www.apostrophitis.at
              STANDAR_D_  - ist das wirklich so schwer?
              1. Sup!

                Kannst Du denn

                ---
                char string[21];

                scanf("%20s",&string);
                /* oder */
                scanf("%20c",&string);

                string[20]='\0';
                ---

                austricksen?

                Wenn nicht, dann kannst Du das zeichenweise Einlesen auch wieder vergessen...

                Gruesse,

                Bio

                --
                Never give up, never surrender!!!
                1. Hallo,

                  Kannst Du denn


                  char string[21];

                  scanf("%20s",&string);
                  /* oder */
                  scanf("%20c",&string);

                  string[20]='\0';

                  austricksen?

                  Nein,aber ich dachte, ich hätte das Tastaturbufferproblem umgangen, was aber doch nicht der Fall war.
                  Es muss doch irgendwie gehen, den Tastaturbuffer zu deaktivieren?
                  An setvbuf (FILE *stream, char *buf, int mode, size_t size) habe ich zwar schon versucht, herumzuschrauben, aber ich glaube nicht, dass das für stdin funktioniert. Zumindest weiß ich bisher nicht wie.

                  Markus.

                  --
                  http://www.apostrophitis.at
                  STANDAR_D_  - ist das wirklich so schwer?
                  1. Moin!

                    Hallo,

                    Kannst Du denn

                    char string[21];

                    scanf("%20s",&string);
                    /* oder */
                    scanf("%20c",&string);

                    string[20]='\0';

                    
                    > >   
                    > > austricksen?  
                    >   
                    > Nein,aber ich dachte, ich hätte das Tastaturbufferproblem umgangen, was aber doch nicht der Fall war.  
                    > Es muss doch irgendwie gehen, den Tastaturbuffer zu deaktivieren?  
                      
                    Du willst deine Zeichen eins zu eins direkt von der Tastatur lesen? – Da wirst du wohl auf andere Methoden zurückgreifen müssen, denn die Funktionen der C-Bibliothek bekommen ohne besondere Vorkehrungen die Eingabe von stdin erst dann, wenn du Enter/Return betätigt hast, da das Zeichen `'\n'`{:.language-c} gleichzeitig einen Flush des Eingabepuffers bewirkt.  
                      
                    Falls du unter einem Unix(-kompatiblen) System arbeitest, empfehle ich dir für den gewünschten Zweck die readline-Bibliothek, unter Windows gibt es den Header io.h, der solche Funktionen wie readkey zur Verfügung stellen sollte.  
                      
                    Viele Grüße,  
                    Robert
                    
                    1. Hallo,

                      Du willst deine Zeichen eins zu eins direkt von der Tastatur lesen?

                      Eigentlich wollte ich nichts anderes, als dass durch die Bufferung nicht der letzte Wert inder nächsten Variable steht, aber zumindest für heute geb ich's auf, da ich jetzt Stunden wegen einer dämlichen Eingabe sitze.
                      Mich würde schon brennend interessieren, wie das bei anderen Konsolenprogramme realisiert ist, nicht einen Buffer Overflow zu erzeugen (zB fgets), aber trotzdem die überschüssigen Werte los zu sein.

                      Markus.

                      --
                      http://www.apostrophitis.at
                      STANDAR_D_  - ist das wirklich so schwer?
                      1. Moin!

                        Hallo,

                        Du willst deine Zeichen eins zu eins direkt von der Tastatur lesen?

                        Eigentlich wollte ich nichts anderes, als dass durch die Bufferung nicht der letzte Wert in der nächsten Variable steht, aber zumindest für heute geb ich's auf, da ich jetzt Stunden wegen einer dämlichen Eingabe sitze.

                        Was heißt das genau? Liest du bei mehrfachen fgets-Aufrufen die gleichen Zeichen? Ich habe folgendes kleine Testprogramm geschrieben, welches exakt die Eingabe in 9-Byte-Blöcken ausgibt:

                          
                        #include <stdio.h>  
                        #include <string.h>  
                          
                        int main(void) {  
                                char buff[10];  
                          
                                /* lesen */  
                                fgets(buff, sizeof buff, stdin);  
                          
                                while (! feof(stdin)) {  
                                        printf("Block: '%s' (%u)\n", buff, strlen(buff));  
                          
                                        fgets(buff, sizeof buff, stdin);  
                                }  
                          
                                return 0;  
                        }  
                        
                        

                        Du kannst es ja einmal probieren und mir dann mitteilen, ob es bei dir funktioniert hat.

                        Mich würde schon brennend interessieren, wie das bei anderen Konsolenprogramme realisiert ist, nicht einen Buffer Overflow zu erzeugen (zB fgets), aber trotzdem die überschüssigen Werte los zu sein.

                        Wie gesagt, für dieses Verhalten gäbe es sonst nur die Erklärung, dass der Eingabepuffer zu klein ist oder das Newline nicht mitgeliefert wird, was allerdings sein sollte.

                        Viele Grüße,
                        Robert

                        1. Hallo Robert,

                          Was heißt das genau? Liest du bei mehrfachen fgets-Aufrufen die gleichen Zeichen? Ich habe folgendes kleine Testprogramm geschrieben, welches exakt die Eingabe in 9-Byte-Blöcken ausgibt:

                          das wird Markus nicht weiterhelfen, denke ich.
                          Wenn ich ihn richtig verstanden habe, will er aus einer Eingabezeile immer nur die ersten n Zeichen verarbeiten, egal wie lang die Zeile tatsächlich ist. Dass er dazu trotzdem die gesamte Zeile lesen muss, ist klar. Dass er sich dabei Gedanken um Buffer Overflow und ähnliche Probleme macht, ist IMHO auch einleuchtend.

                          Wie gesagt, für dieses Verhalten gäbe es sonst nur die Erklärung, dass der Eingabepuffer zu klein ist oder das Newline nicht mitgeliefert wird, was allerdings sein sollte.

                          Das Problem ist wirklich nur, wenn ich es richtig sehe, dass man die Länge der Eingabezeile nicht vorhersehen kann. Die kann beliebig lang sein, man muss also eine unbestimmte Anzahl von Zeichen zwar aus dem Eingabepuffer (nicht Tastaturpuffer, Markus!!) abholen, aber ignorieren.

                          Schönen Abend noch,
                           Martin

                          --
                          Irgendwann in grauer Vorzeit benutzte einer unserer prähistorischen Vorfahren ein Schimpfwort anstelle der Keule.
                          Die Zivilisation hatte begonnen.
                          1. Hallo,

                            Wenn ich ihn richtig verstanden habe, will er aus einer Eingabezeile immer nur die ersten n Zeichen verarbeiten, egal wie lang die Zeile tatsächlich ist. Dass er dazu trotzdem die gesamte Zeile lesen muss, ist klar. Dass er sich dabei Gedanken um Buffer Overflow und ähnliche Probleme macht, ist IMHO auch einleuchtend.

                            Ja, genauso meine ich es. Aufgrund der Möglichkeit von Buffer Overflows wollte ich eben die Strings nur zeichenweise einlesen.

                            Das Problem ist wirklich nur, wenn ich es richtig sehe, dass man die Länge der Eingabezeile nicht vorhersehen kann. Die kann beliebig lang sein, man muss also eine unbestimmte Anzahl von Zeichen zwar aus dem Eingabepuffer (nicht Tastaturpuffer, Markus!!) abholen, aber ignorieren.

                            Ja, was genau versteht man nun unter dem Eingabepuffer, bzw wie könnte ich das realisieren?

                            Markus.

                            --
                            http://www.apostrophitis.at
                            STANDAR_D_  - ist das wirklich so schwer?
                            1. Sup!

                              Lies halt einfach nur die ersten neun Zeichen, und dann:

                              while (getc()!= '\n') {}

                              Gruesse,

                              Bio

                              --
                              Never give up, never surrender!!!
                              1. Hallo,

                                ich glaube getc ist das, was ich gesucht habe.  Ich habe probehalber eine Endlossschleife um die Eingabeschleife gebaut, und es wird die Eingabe beim nächsten Durchlauf nicht überschrieben.

                                  
                                  
                                char nmbr1[11];  
                                  
                                while(1)   {  
                                i = 0;  
                                  
                                    while ((c = getc(stdin)) != '\n') {  
                                        if (i < 10)  {  
                                        nmbr1[i] = c;  
                                        nmbr1[i+1] = '\0';  
                                        }  
                                    i++;  
                                    }  
                                printf("output: %s\n", nmbr1);  
                                }  
                                  
                                
                                

                                Markus.

                                --
                                http://www.apostrophitis.at
                                STANDAR_D_  - ist das wirklich so schwer?
                  2. Sup!

                    Was stört Dich am Tastaturbuffer?

                    Gruesse,

                    Bio

                    --
                    Never give up, never surrender!!!
              2. Ich glaub's nicht. Sogar hier ist der Tastaturbuffer im Weg, also ist es im Prinzip nicht besser als fgets.

                Markus.

                --
                http://www.apostrophitis.at
                STANDAR_D_  - ist das wirklich so schwer?
              3. n'Abend Markus,

                ich glaube, dass ich jetzt eine Möglichkeit gefunden habe, die man nicht austricksen kann:

                einen hab ich noch! Wenn du nämlich hier...

                i++;

                }
                str[10] = '\0';

                  
                das Ende deiner Schleife änderst in  
                  
                
                > ` str[i] = 0;`{:.language-c}  
                  
                dann klappt es auch \_sicher\_, wenn die Eingabe kürzer als 10 Zeichen ist. Andernfalls verlässt du dich darauf, dass str[] mit Nullbytes initialisiert wird. Du setzt das abschließende Nullbyte einfach ans Ende des Puffers, ohne zu berücksichtigen, wieviele Zeichen tatsächlich hineingeschrieben wurden.  
                  
                Schönen Abend noch,  
                 Martin  
                
                -- 
                why the heck do you jerk think, that wir ein doppelposting nicht bemerken, wenn you zwischendurch the sprache wechselst?  
                  (wahsaga, http://forum.de.selfhtml.org/?t=110904&m=697006, nicht archiviert)