Pełen artykuł

Transkrypt

Pełen artykuł
Dokument pobrany z serwisu: www.Centrum.Bezpieczenstwa.pl
Autor artykułu: Marcin Ulikowski (www.nfsec.pl)
Integer overflow
KaŜdy typ w języku C moŜe być traktowany jako liczba. KaŜdy typ danych ma określoną długość
wyraŜoną w bitach lub bajtach. Dla przykładu typ char, czyli typ znakowy ma długość 8 bitów,
czyli 1 bajta. Typ integer, który jest typem liczbowym ma długość 32 bitów lub 4 bajtów. Wartość
jaką moŜe przenosić dany typ jest ograniczona jego długością oraz od tego czy jest ze znakiem
(signed) czy bez (unsigned). Polecam zapoznać się z małym plikiem /usr/include/limits.h, aby
dowiedzieć się o limitach dla konkretnych typów danych. PoniŜszy kod pokaŜe rozmiary
podstawowych typów. NaleŜy jednak wiedzieć, Ŝe chociaŜ poniŜsze typy mają róŜną długość to na
stosie / stercie zajmują zawsze po 32 bity (4 bajty), co moŜna łatwo sprawdzić uŜywając narzędzia
gdb. Oczywiście przy załoŜeniu, Ŝe uŜywamy CPU zgodnego z architekturą Intel IA32.
[elceef@osiris ~]$ cat sizes.c
#include
int main(void) {
char a;
short b;
int
c;
printf("char (%d bits)n", sizeof(a) * 8);
printf("short (%d bits)n", sizeof(b) * 8);
printf("int
(%d bits)n", sizeof(c) * 8);
return 0;
}
[elceef@osiris ~]$ gcc -Wall -o sizes sizes.c
[elceef@osiris ~]$ ./sizes
char (8 bits)
short (16 bits)
int
(32 bits)
W kolejnych przykładach będziemy uŜywać typu char zamiast integer. Łatwiej jest zrozumieć
problem kiedy będziemy operować na małych liczbach oraz na małych zakresach. Natomiast w
przypadku typu integer i innych typów całkowitych cała zasada "działania" pozostaje bez zmian.
Zmienne typu signed (ze znakiem):
[elceef@osiris ~]$ cat example1.c
#include
#include
int main(int argc, char *argv[]) {
char number;
if (argc < 2) exit(1);
number = atoi(argv[1]);
printf("number
= %dn", number);
number++;
printf("number + 1 = %dn", number);
return 0;
}
[elceef@osiris ~]$ gcc -Wall -o example1 example1.c
[elceef@osiris ~]$ ./example1 127
number
= 127
number + 1 = -128
Dlaczego po zwiększeniu 127 o 1 - liczba zmieniła znak na przeciwny? PoniewaŜ został
przekroczony zakres liczb dodatnich. Zmienna char, którą zadeklarowaliśmy była domyślnie ze
znakiem (signed). Kiedy miała wartość 127 binarnie wyglądała w ten sposób:
01111111
Pierwszy bit czyli 0 oznacza znak dodatni (jest to bit znaku). Kolejne jedynki to juŜ wartość samej
zmiennej czyli 127. Oto jak wygląda liczba po dodaniu jedynki:
01111111
+ 00000001
---------10000000
Widać, Ŝe pierwszy bit zmienił wartość na 1, czyli liczba ma teraz znak ujemny. Jest to takŜe
najmniejsza liczba ujemna jaką moŜna zapisać uŜywając tego typu danych (signed char). NaleŜy
zwrócić uwagę, Ŝe teraz siedem zer oznacza liczbę -128 ze względu na inny zakres liczb ujemnych
w stosunku do dodatnich. Liczby ujemne mają zakres <-128;-1>, natomiast dodatnie <0;127>
dlatego siedem jedynek będzie oznaczało wartość -1. Dla pewności przykłady:
00000000
- 00000001
---------11111111 = (-1)
01111111
+ 00000010
---------10000001 = (-127)
Podsumowując typ signed char moŜne przenosić liczby z zakresu <-128;127>. Jest to 256 liczb
czyli
2^8.
Zmienne typu unsigned (bez znaku)
[elceef@osiris ~]$ cat example2.c
#include
#include
int main(int argc, char *argv[]) {
unsigned char number;
if (argc < 2) exit(1);
number = atoi(argv[1]);
printf("number
= %dn", number);
number++;
printf("number + 1 = %dn", number);
return 0;
}
[elceef@osiris ~]$ gcc -Wall -o example2 example2.c
[elceef@osiris ~]$ ./example2 255
number
= 255
number + 1 = 0
Zmienna typu unsigned char miała wartość 255. Po zwiększeniu o 1 zmieniła wartość na 0,
poniewaŜ został przekroczony limit.
Limit
= 255 lub 11111111
Wartość = 256 lub 100000000
Wynik
= 0
lub 00000000
Do obliczenia wyniku takiej operacji moŜemy posłuŜyć się dość znanym wzorem:
wartosc % (limit + 1) = wynik
czyli
256 % (255 + 1) = 0
lub inny przykład
257 % (255 + 1) = 1
Limit
= 255 lub 11111111
Wartosc = 257 lub 100000001
Wynik
= 1
lub 00000001
Typ unsigned char moŜe przenosić liczby z zakresu <0;255> Jest to 256 liczb czyli 2^8
Integer overflows chociaŜ trudne do wykrycia z reguły nie stanowią same w sobie duŜego
zagroŜenia. Jednak czasami mogą prowadzić do innych błędów, najczęściej przepełnień bufora
powodując zapis w obszarach pamięci o krytycznym znaczeniu. Wykorzystanie ujemnej zmiennej
jako argumentu malloc() nie spowoduje błędu, a jedynie zaalokowanie kilkubajtowego bufora,
który moŜe zostać nadpisany.

Podobne dokumenty