C/Błędy

Z Wikibooks, biblioteki wolnych podręczników.
< C
Skocz do: nawigacja, szukaj

Definicje[edytuj]

Błąd (oprogramowania, programu) (ang. bug) jest to usterka programu komputerowego powodująca jego nieprawidłowe działanie[1]

Sygnał (ang. signal) jest to informacja przesyłana do procesu.

Wyjątek (ang. exception) jest to przewidziany przez programistę błąd programu [2]

Typy[edytuj]

Ze względu na przyczynę błędy dzieli się na trzy główne typy:

  • błędy składniowe – nie pozwalają na kompilację programu, jak np. literówka w wywołaniu zmiennej. Są one najczęściej dość łatwe do usunięcia, zazwyczaj wynikają z drobnych pomyłek programisty.
  • błędy semantyczne (znaczeniowe) – część błędów semantycznych można wychwycić już w momencie kompilacji, np. próbę odwołania się do nieistniejącej funkcji, lecz inne mogą ujawnić się dopiero w trakcie wykonywania.
  • błędy logiczne – nie przerywają kompilacji, lecz powodują niewłaściwe działanie warstwy logicznej jak np. niepoprawne wyznaczanie pozycji gracza (które także może być spowodowane literówką – jak np. wpisanie znaku „+” zamiast „-”). Ten typ błędów jest znacznie trudniejszy do wykrycia i usunięcia; często błąd tkwi w jednym źle zapisanym znaku, lecz programista musi do tego znaku sam dojść (w przypadku błędu składniowego znak jest wskazywany przez komunikat kompilatora).

Ze względu na moment wykrycia błędy dzielimy na:

  • błędy wykryte przy kompilacji ( buildtime)
  • błędy wykryte przy uruchomieniu ( runtime)
  • błędy niewykryte, czyli ukryte ( program kompiluje się poprawnie i uruchamia się bez komunikatów o błędach)

Przykłady typowych błędów[3][4]

Błędy przy kompilacji[edytuj]

opis błędów kompilacji

Błędy przy uruchamianiu[edytuj]

Błędy przy uruchamianiu programu (błędy wykonania, ang. run-time errors)

Naruszenie ochrony pamięci[edytuj]

Przepełnienie stosu[edytuj]

Może być spowodowane:

  • rekursją: nieskończoną lub zbyt głęboką
  • rozmiarem zmiennych statycznych (stosu)
Rekursja[edytuj]

Program powoduje nieskończoną rekursję:

#include <stdio.h>


int foo() {
      return foo();
}
 
 
int main ()
{
  foo();


return 0;
}

Program kompiluje się bez problemów:

 gcc c.c -Wall

ale jego uruchomienie powoduje błąd:

 ./a.out
 Naruszenie ochrony pamięci
Rozmiar zmiennych[edytuj]

Poniższy program tworzy styczne tablice o coraz większym rozmiarze aż do powstania błędu związanego z dostępem do pamięci:

#include <stdio.h>


void TestArray(int length)
{

 double n[length]; /* n is an array of 10 integers */
   int i;
 
   /* initialize elements of array n to 0 */        
   for ( i = 0; i < length; i++ )
   {
      n[ i ] = i *100.0; /* set element at location i to i + 100 */
   }

   n[1]=n[1]; // to remove warning: variable ‘n’ set but not used [-Wunused-but-set-variable

   // http://stackoverflow.com/questions/37538/how-do-i-determine-the-size-of-my-array-in-c
   // length = sizeof(n)/sizeof(n[0])
   printf(" length = %d , size = %ld bytes = %ld kB  \n",length, length*sizeof(double), length*sizeof(double)/1000);
}
  
 
int main ()
{
  int i;
  for (i=1; i<1000000; i++)
      TestArray(1000*i);
  return 0;
}

Tablice statyczne są zapisywane w stosie więc następuje przepełnienie stosu[12] : Wynik:

length = 1043000 , size = 8344000 bytes = 8344 kB 
 length = 1044000 , size = 8352000 bytes = 8352 kB 
 length = 1045000 , size = 8360000 bytes = 8360 kB 
 length = 1046000 , size = 8368000 bytes = 8368 kB 
 length = 1047000 , size = 8376000 bytes = 8376 kB 
Naruszenie ochrony pamięci


Co dziwne największa możliwa do utworzenia tablica ma rozmiar 8376 kB, co jest większe niż ustalony maksymalny rozmiar stosu:

ulimit -s

otrzymujemy

8192


Potencjalne rozwiązania problemu:

  • zmiana zmiennych na dynamiczne
  • zmiana limitu wielkości stosu
  • zmiana algorytmu

Przepełnienie bufora[edytuj]

Program wczytuje znaki

/*
  http://www.tenouk.com/Bufferoverflowc/Bufferoverflow1.html
  The output, when the input is: 12345678 (8 bytes), the program run smoothly.

  When the input is: 123456789 (9 bytes), then :
  * the following will be displayed when compiled with Microsoft Visual C++ 6.0. 
  * In Linux the “Segmentation fault” message will be displayed and the program terminates.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  // theoretically reserve 5 byte of buffer plus the
  // terminating NULL....should allocate 8 bytes = 2 double words,
  // to overflow, need more than 8 bytes...
  // so, if more than 8 characters input by user,
  // there will be access violation, segmentation fault etc.
  char mybuffer[5];
  // a prompt how to execute the program...
  if (argc < 2)
    {
      printf("strcpy() NOT executed....\n");
      printf("Syntax: %s <characters>\n", argv[0]);
      exit(0);
    }
  // copy the user input to mybuffer, without any bound checking
  // a secure version is srtcpy_s()
  strcpy(mybuffer, argv[1]);
  printf("mybuffer content= %s\n", mybuffer);
  // you may want to try strcpy_s()
  printf("strcpy() executed...\n");
  return 0;
}

Kompilujemy program bez problemów:

 gcc b.c -Wall

Uruchamiamy:

 ./a.out

Wynik:

 strcpy() NOT executed....
 Syntax: ./a.out <characters>

Zapomnieliśmy wprowadzić dane, program dopomina się o nie. Ponownie uruchamiamy:

./a.out 1234

Wynik:

 mybuffer content= 1234
 strcpy() executed...

bez błędów.

./a.out 12345678

mybuffer content= 12345678
 strcpy() executed...

Wprowadzenie więcej niż 8 znaków:

 ./a.out 123456789

powoduje błąd:

 mybuffer content= 123456789
 strcpy() executed...
 *** stack smashing detected ***: ./a.out terminated
 Przerwane

Zapobieganie[edytuj]

Dokumentacja[edytuj]

Standardy kodowania[edytuj]

Wcięcia (ang. Indent style[18]):

  • kernel [19]
  • K&R
  • GNU

Automatyczne formatowanie:

  • style Emacsa (automatyczne formatowanie) [20]

Bezpieczne programowanie[edytuj]

W C nie ma automatycznego:

  • sprawdzania zakresów indeksu tablicy (ang. no bounds checks on array)
  • wskaźników (ang. pointer references)

Funkcje, których należy unikać dotyczą: [21]

  • operacji na łańcuchach


Niebezpieczne funkcje i ich bezpieczne zamienniki[21]
niebezpieczna opis ryzyka bezpieczna
gets() najbardziej niebezpieczna fgets(buf, size, stdin)
strcpy bardzo niebezpieczna strncpy
strcat bardzo niebezpieczna strncat
sprintf bardzo niebezpieczna snprintf asprintf
scanf bardzo niebezpieczna Use precision specifiers, or do your own parsing.
sscanf bardzo niebezpieczna Use precision specifiers, or do your own parsing.
fscanf bardzo niebezpieczna Use precision specifiers, or do your own parsing.
vfscanf bardzo niebezpieczna Use precision specifiers, or do your own parsing.
vsprintf bardzo niebezpieczna Use vsnprintf instead, or use precision specifiers.
vscanf bardzo niebezpieczna Use precision specifiers, or do your own parsing.
vsscanf bardzo niebezpieczna Use precision specifiers, or do your own parsing.
streadd bardzo niebezpieczna Make sure you allocate 4 times the size of the source parameter as the size of the destination.
strecpy bardzo niebezpieczna Make sure you allocate 4 times the size of the source parameter as the size of the destination.
strtrns Risky Manually check to see that the destination is at least the same size as the source string.
realpath Very risky (or less, depending on the implementation) Allocate your buffer to be of size MAXPATHLEN. Also, manually check arguments to ensure the input argument is no larger than MAXPATHLEN.
syslog Very risky (or less, depending on the implementation) Truncate all string inputs at a reasonable size before passing them to this function.
getopt Very risky (or less, depending on the implementation) Truncate all string inputs at a reasonable size before passing them to this function.
getopt_long Very risky (or less, depending on the implementation) Truncate all string inputs at a reasonable size before passing them to this function.
getpass Very risky (or less, depending on the implementation) Truncate all string inputs at a reasonable size before passing them to this function.
getchar Moderate risk Make sure to check your buffer boundaries if using this function in a loop.
fgetc Moderate risk Make sure to check your buffer boundaries if using this function in a loop.
getc Moderate risk Make sure to check your buffer boundaries if using this function in a loop.
read Moderate risk Make sure to check your buffer boundaries if using this function in a loop.
bcopy Low risk Make sure that your buffer is as big as you say it is.
fgets Low risk Make sure that your buffer is as big as you say it is.
memcpy Low risk Make sure that your buffer is as big as you say it is.
snprintf Low risk Make sure that your buffer is as big as you say it is.
strccpy Low risk Make sure that your buffer is as big as you say it is.
strcadd Low risk Make sure that your buffer is as big as you say it is.
strncpy Low risk Make sure that your buffer is as big as you say it is.
vsnprintf Low risk Make sure that your buffer is as big as you say it is.


słowa kluczowe:

Wykrywanie i usuwanie[edytuj]

testy programu[edytuj]

Liczbę błędów można też ograniczyć przeprowadzając testy programu.

Komunikaty o błędach[edytuj]

Obsługa błędów i wyjątków[edytuj]

W języku C są funkcje i makra do kontroli błędów:[22][23]

Źródła[edytuj]

  1. błąd w wikipedii
  2. Z Lipiński : Programowanie w Cpp
  3. programowanie-pułapki-jezyka-c
  4. c errors
  5. wikipedia: Naruszenie ochrony pamięci
  6. W Sikora-Kobyliński, M Czępiński, G Kulewski : Ostatnia linia obrony przed atakiem
  7. ...definition of "Segmentation Fault" - Where is...
  8. Przepełnienie bufora w wikipedii
  9. Open Web Application Security Project
  10. Smashing The Stack For Fun And Profit by Aleph One
  11. Journey to the Stack, Part I by Gustavo Duarte Mar 10th, 2014
  12. Przepełnieniestosu w ang wikipedii
  13. Doxygen Magically Turns Source Code into Documentation by Carla Schroder
  14. sphinx-doc
  15. CERT C Coding Standard
  16. Programowanie: Sekrety profesjonalnego programowania - Gandalf
  17. GNU Coding Standards
  18. Indent_style - angielska Wikipedia
  19. Kernel coding style
  20. wcięcia w c - Emacs
  21. 21,0 21,1 Protect your code through defensive programming by Gary McGraw, John Viega
  22. Simiński: Programowanie w C++
  23. libc: Errors in Floating-Point Calculations
  24. opengroup: fenv.h
  25. libc: FP Exceptions