You are on page 1of 6

Gynvael Coldwind

Diabe tkwi w szczegach: C/C++ (cz 1)


Programujc w jzykach C lub C++, bardzo atwo jest popeni bd. Powodw mona wymieni kilka. Po pierwsze, oba jzyki s stosunkowo niskopoziomowe, a wic wymagaj rcznego zarzdzania pamici dynamiczn, dbania o poprawno wskanikw, jak i dbania o wiele innych aspektw. Po drugie, nie wszystkie zachowania obu jzykw s w peni zdefiniowane - standardy C i C++ wskazuj zachowania nie zdefiniowane w ogle (ang. Undefined Behavior, dalej UB), nie zdefiniowane w standardzie, ale definiowane przez konkretne implementacje (ang. Implementation-Defined Behavior), oraz takie, ktre mog zosta zaimplementowane na wicej ni jeden sposb, ale niekoniecznie musz zosta jawnie zdefiniowane w dokumentacji (ang. Unspecified Behavior). Niniejszy artyku ma na celu przedstawienie czytelnikowi kilku przykadw, w ktrych pomimo iluzorycznej trywialnoci kodu, znajduj si subtelne bdy lub niejasnoci.

INTEGER OVERFLOW
Arytmetyka w C/C++ jest wietnym przykadem pozornej prostoty. Niestety, w operacjach takich jak dodawanie, odejmowanie, mnoenie, dzielenie czy nawet zmiana znaku kryje si do spora liczba kruczkw, na ktre trzeba uwaa. Wikszo z nich mona zrzuci na karb zjawiska zwanego integer overflow (tumaczone czasem na polski jako przekroczenie zakresu liczb cakowitych [1], cho termin angielski jest bardziej popularny). Integer overflow to zjawisko wystpujce, gdy wynik danej operacji arytmetycznej wykracza poza zakres danego typu, czyli np. w przypadku typu int jest wikszy ni INT_MAX lub mniejszy ni INT_MIN. Czasami rozrnia si integer overflow jako przekroczenie zakresu od gry, oraz integer underflow jako przekroczenie zakresu od dou. Listing 1a ilustruje kilka przykadowych sytuacji, w ktrych nastpuje przekroczenie zakresu. Listing 1a. Integer overflow
/* Platforma: x86, 32-bit, GCC */ int a = 2147483647; /* INT_MAX */ a++; /* wynik 2147483648 wykracza poza zakres */ unsigned char b = 123; b *= b; /* wynik 15129 wykracza poza zakres */ short c = -32767; c -= 123; /* wynik -32890 wykracza poza zakres */

Standardy i dokumentacje
Kompilatory oraz interpretery jzykw C oraz C++ s implementowane wg tzw. standardw lub szkicw standardw (ang. draft). Obowizujcymi, a zarazem najnowszymi standardami s: ISO/IEC 9899:2011 (zwany potocznie C11; odpowiadajcy mu draft to N1570 [S1]), ISO/IEC 14882:2011 (zwany potocznie C++11; odpowiadajcy mu draft to N3337 [S2]). [W niektrych miejscach w niniejszym artykule powouj si na konkretne sekcje z najnowszych szkicw standardw - takie miejsca zostan oznaczone numerem szkicu oraz numerem konkretnej sekcji (np. [N1570 1.2.3] oznacza najnowszy draft C11, sekcja 1.2.3)] Oprcz powyszych dokumentw warto rwnie wskaza dokumentacje samych kompilatorw oraz interpreterw, m.in. z uwagi na zawarte w nich informacje o przyjtych rozwizaniach dla niezdefiniowanych zachowa oraz zachowa definiowanych przez implementacje. Dokumentacje kompilatorw zawieraj rwnie informacje o rozszerzeniach jzyka oferowanych przez dany kompilator (rozszerzenia mog uatwi prac programicie, ale niestety sprawiaj, e kod przestaje by przenony midzy kompilatorami). Przykadowo kompilatory z rodziny GCC oraz firmy Microsoft posiadaj bardzo dobr dokumentacj (kolejno [D1] oraz [D2]).

Problem integer overflow w C/C++ rozwizany jest w nastpujcy sposb:

PPW przypadku zmiennych typu unsigned (np. unsigned int) wynik jest zawsze obliczany modulo grna granica + 1 (chocia zazwyczaj wygodnie jest myle o tym jako o obcinaniu wyniku do dolnych N bitw, gdzie
/www.programistamag.pl/

21

JZYKI PROGRAMOWANIA
Nie tylko C/C++...
Problem integer overflow oczywicie nie dotyczy jedynie jzykw C/C++ czy architektury x86 - w zasadzie kady jzyk czy architektura musi w jaki sposb poradzi sobie z tym zjawiskiem. Do popularnych rozwiza nale: PP nasycenie (ang. saturation) - zmienna nasyca si w wartociach granicznych, czyli np. wynikiem dziaania INT_ MAX+1 jest nadal INT_MAX; przykadowo architektura x86 (rozszerzenie MMX) oraz ARMv6 udostpniaj arytmetyk nasycon PP "zawinicie" (ang. wrap) / modulo - po najwikszej moliwej wartoci zawsze nastpuje najmniejsza moliwa warto, a wic wynikiem dziaania INT_MAX+1 jest INT_ MIN; jest to popularne zachowanie w przypadku platformy x86 oraz np. jzyka Java PP wyjtek / wizy integralnoci - w przypadku przekroczenia zakresu zmiennej w wyniku dziaania wygenerowany zostaje wyjtek (lub naruszenie wizw integralnoci jest sygnalizowane w inny sposb); przykadem moe by jzyk Ada PP zmiana typu zmiennej na typ zmiennoprzecinkowy a wic wynik dziaania INT_MAX+1 bdzie faktycznie poprawny (w matematycznym rozumieniu dodawania), jednak zmienna zmieni swj typ na float; przykadem moe by jzyk PHP PP zwikszenie rozmiaru zmiennej - w takim wypadku trudno mwi o zjawisku integer overflow - gdy wynik dziaania wykracza poza zakres zmiennej, jest ona po prostu powikszana; przykadem moe by jzyk Python

N to wielko zmiennej w bitach), a wic przykadowo UINT_MAX + 1 da w wyniku 0 dla zmiennej typu unsigned int (poniewa wynik obcity do dolnych 32-bitw wyniesie w tym wypadku zero). PPW przypadku zmiennych typu signed (np. int) zachowanie przy przekroczeniu zakresu jest niezdefiniowane (patrz ramka: UB a kod wynikowy). Warto nadmieni, e niektre implementacje traktuj signed integer overflow analogicznie do unsigned integer overflow, czyli traktuj dolne N bitw wyniku jako ostateczny wynik. Niestety, przycinanie wyniku ma w niektrych przypadkach powane konsekwencje dla bezpieczestwa aplikacji. Dzieje si tak, poniewa wynik danego obliczenia jest niepoprawny z punktu widzenia tej samej operacji wykonanej, korzystajc z normalnych matematycznych

zasad, a wic moe by niezgodny z przewidywaniami programisty, a zatem z tym, co programista faktycznie chcia osign. Listing 1b. Niepoprawny odbir testu ze strumienia
/* platforma: x86, 32-bit, Windows */ /* odbir dugoci tekstu */ /* max dugo tekstu: 255 */ unsigned char text_size = read_byte(input); /* wyliczenie wielkoci bufora */ unsigned char buffer_size = text_size + 1; /* alokacja */ char *text = (char*)malloc(buffer_size); if(!text) abort(); /* odbir tekstu */ read_data(text, text_size); text[text_size] = '\0'; ...

UB a kod wynikowy
Natrafiajc na kod powodujcy pojawienie si niezdefiniowanego zachowania, kompilator nie jest zobowizany przez standard do (prawie) adnego konkretnego zachowania (niektrzy artuj, e nawet wygenerowanie kodu formatujcego dysk jest w takim wypadku poprawne wzgldem standardu [6]). Zarwno standard C, jak i C++ wskazuj nastpujcy zakres sugerowanych zachowa [N1570 3.4.3] [N3337 1.3.24]: PP cakowite zignorowanie sytuacji - czyli wygenerowanie kodu tak jakby danego dane UB nie byo w tym miejscu moliwe (jest to tosame ze zdaniem si w tym konkretnym wypadku na zachowanie specyficzne dla danej platformy) PP przyjcie udokumentowanego rozwizania sytuacji PP przerwanie kompilacji z bdem PP przerwanie wykonania programu z bdem Dodatkowo niektre popularne kompilatory w ramach optymalizacji decyduj si nie generowa kodu wynikowego na podstawie kodu opartego o UB.

Aby zilustrowa zagroenie, posu si fragmentem kodu widocznym na Listingu 1b. Jest to do typowy (i niestety niepoprawny) kod odbierajcy krtki tekst ze strumienia. Dziaa on w nastpujcy sposb: 1. Odbiera dugo tekstu. 2. Wylicza wielko bufora (czyli do wielkoci tekstu dodawany jest jeden bajt potrzebny, aby zakoczy odbierany string terminatorem '\0'). 3. Alokuje bufor na ostateczny tekst wg wyliczonej wielkoci. 4. Wczytuje faktyczny tekst ze strumienia do bufora. Integer overflow w tym wypadku nastpuje w sytuacji, gdy przesana wielko tekstu wynosi 255, czyli UCHAR_MAX dla rozwaanej platformy. Stosujc opisan wyej zasad, ostateczna wielko buffer_size bdzie wynosi:
(255 + 1) % 256 0

22

/3 . 2012 . (3) /

DIABE TKWI W SZCZEGACH: C/C++, CZ 1

W tym momencie powstaje pytanie: jak funkcja malloc zareaguje na 0 jako ilo bajtw do zaalokowania? Standard C wskazuje, e zachowanie w tym wypadku jest zdefiniowane przez konkretn implementacj [N1570 7.22.3], natomiast standard C++ wymaga, aby zwrcony wskanik by rny od nullptr [N3337 3.7.4.1]. Dodatkowo, dla rozwaanej platformy przypadek ten jest opisany w MSDN [2] na stercie zaalokowany zostanie bufor o wielkoci zerowej. A wic malloc si powiedzie i zwrci poprawny wskanik do bufora rny od nullptr/NULL. Ostatecznie nastpuje odbir tekstu i zapisanie go do wczeniej zaalokowanego bufora. Niestety, wywoanie read_data korzysta ze zmiennej text_size do okrelenia wielkoci odczytywanych danych, a wic w tym wypadku do bufora o wielkoci 0 zapisane zostanie 255 bajtw tak wic konsekwencj integer overflow jest przepenienie bufora na stercie (ang. heap-based buffer overflow), co w skrajnym przypadku moe doprowadzi do wykonania kodu wstrzyknitego przez atakujcego (tematyka ta jednak mocno wykracza poza zakres niniejszego artykuu; zainteresowanym polecam [3] oraz [4]). Jak wic zabezpieczy kod w takim przypadku? Oczywicie naley sprawdzi, czy w wyniku danej operacji moe nastpi przekroczenie zakresu (test przed) lub wykona operacje i sprawdzi, czy przekroczenie zakresu faktycznie nastpio (test po). Przykadowa funkcja realizujca inkrementacj zmiennej typu unsigned char ze zwrceniem uwagi na moliwe przekroczenie zakresu znajduje si na Listingu 1c. Funkcja dziaa w nastpujcy sposb: jeli przepenienie nastpio, to warto po inkrementacji bdzie nisza (a konkretniej, bdzie rwna 0), ni warto warto przed. Konkretniej w tym wypadku byoby to:
if(UCHAR_MAX > 0) ...

Listing 1d. Przykadowa niepoprawna funkcja inkrementujca zmienn typu int


bool i_inc(int &a) { /* czy nastpi przepenienie */ if(a > a + 1) { return false; } a++; return true; }

Jak wspomniaem wczeniej, integer oveflow dla typw signed jest zachowaniem niezdefiniowanym. Co wicej, niektre popularne kompilatory podczas optymalizacji zakadaj, e overflow nigdy miejsca mia nie bdzie (poniewa ufaj, e programista nigdy nie dopuci do UB). A wic w tym konkretnym wypadku kompilator moe zaoy, e warunek a > a + 1 nigdy nie bdzie prawdziwy, a nastpnie, korzystajc z tego zaoenia, cakowicie usun ten blok kodu. Dla przykadu, na Listingu 1e oraz 1f znajduje si funkcja i_inc kolejno przed, oraz po optymalizacji (test zosta wykonany, uywajc GCC 4.6.2 z pakietu MinGW [5]) jak wida, sprawdzenie warunku oraz blok if zostay faktycznie usunite. Listing 1e. Porednia forma funkcji i_inc przed optymalizacj
// g++ (GCC) 4.6.2 (MinGW) // -fdump-tree-all -O3 -c test.cpp // plik: test.cpp.013t.cfg ;; Function bool i_inc(int&) (_Z5i_incRi) bool i_inc(int&) (int & a) { bool D.1703; int D.1700; int D.1699; <bb 2>: D.1699 = *a; D.1699 = *a; D.1700 = D.1699 + 1; if (D.1699 > D.1700) goto <bb 3>; else goto <bb 4>; <bb 3>: D.1703 = 0; goto <bb 5>; <bb 4>: D.1699 = *a; D.1700 = D.1699 + 1; *a = D.1700; D.1703 = 1; <bb 5>: return D.1703; }

co oczywicie jest warunkiem prawdziwym, a jednoczenie jedyn sytuacj, w ktrej a faktycznie jest wiksze od a+1. Listing 1c. Przykadowa funkcja inkrementujca zmienn typu unsigned char
bool uc_inc(unsigned char &a) { /* czy nastpi przepenienie */ if(a > a + 1) { return false; } a++; return true; }

Oczywicie nie jest to jedyna poprawna implementacja funkcji inkrementujcej dla zmiennych typu unsigned. Co wane, implementacja ta jest niepoprawna dla zmiennych signed (Listing 1d).

/www.programistamag.pl/

23

JZYKI PROGRAMOWANIA
Listing 1f. Porednia forma funkcji i_inc po optymalizacji
// g++ (GCC) 4.6.2 (MinGW) // -fdump-tree-all -O3 -c test.cpp // plik: test.cpp.143t.optimized bool i_inc(int&) (int & a) { int D.1700; int D.1699; <bb 2>: D.1699_7 = *a_2(D); D.1700_8 = D.1699_7 + 1; *a_2(D) = D.1700_8; return 1; } # Pelles ISO C 7.00.18 ABCDEFGHI XYZ # Borland C++ 5.5.1 ABCDEFGHI ZYX

W zwizku z powyszym, w przypadku zmiennych typu signed nie moemy uy testu po. Naley wic sprawdzi, czy parametr jest rwny granicznej wartoci, poniewa jest to jedyna warto, ktra przy prbie inkrementacji spowoduje przepenienie. Listing 1g prezentuje poprawny test dla tego przypadku. Listing 1g. Poprawny test dla inkrementacji zmiennej typu int
/* czy nastpi przepenienie? */ /* wymaga limits.h */ if(a == INT_MAX) { return false; }

Na Listingu 2a znajduj si dwa proste kawaki kodu w obu z nich wywoywana jest funkcja p() wypisujca podany znak oraz zwracajca warto, ktra jest uywana podczas dalszego dziaania programu. Postawmy wic pozornie proste pytanie: w jakiej kolejnoci zostan wypisane znaki na strumie wyjcia? Zgadywa mona, e wywoania bd nastpowa od lewej do prawej. Lub by moe kolejno wywoa bdzie zalena od matematycznych priorytetw operatorw. Mona rwnie po prostu sprawdzi Rysunek 1 pokazuje wynik wykonania przykadowego kodu na kilku rnych kompilatorach jak wida, testowane implementacje C/C++ wywouj funkcje w rnej kolejnoci [7]. Listing 2b. Przykad niepoprawnego kodu tworzcego wtek
int SpawnThread(func_t *callback, void* userdata); ThreadStruct *GetNewThreadStruct(void); int main() { ... /* stwrz nowy wtek */ ThreadStruct *th; int tid = SpawnThread( th->ThreadFuncPtr, th = GetNewThreadStruct() ); ...

NIEJASNA KOLEJNO WYKONANIA


Listing 3a. Co zostanie wypisane na stdout?
int p(char c) { putchar(c); return 1; } void func(int a, int b, int c) {} ... /* Przypadek 1 int d = p('A') p('D') p('G') putchar('\n'); */ + p('B') * p('C') * + p('E') * (p('F') * + p('H')) - p('I');

Najwaniejsze punkty sekwencyjne w C i C++


Standardy C i C++ definiuj m.in. nastpujce punkty sekwencyjne [8] [N1570 Annex C] [N3337 1.9]: PP na kocu penego wyraenia; do penych wyrae zaliczane s: normalne wyraenia zakoczone rednikiem, warunek w if, switch, while oraz do ... while, kade z (opcjonalnych) wyrae w for(;;), opcjonalne wyraenie przy return, PP bezporednio przed wywoaniem funkcji, ale po ewaluacji argumentw (oraz samego wskanika na funkcje), PP midzy operatorami &&, || oraz , (comma); naley pamita, e separator argumentw funkcji nie jest operatorem comma), PP w wyraeniu a ? b : c midzy operandem a, kolejnym ewaluowanym operandem, PP midzy kolejnymi inicjalizacjami zmiennych: int a = 1, b = 2, PP po penej deklaracji, np. int a. Oprcz powyszych istniej rwnie inne punkty sekwencyjne.

/* Przypadek 2 */ func(p('X'), p('Y'), p('Z'));

Rysunek 1. Wynik wykonania kodu z Listingu 3a


# gcc (GCC) 4.6.2 (MinGW) AEFGHBCDI ZYX # Microsoft C/C++ 15.0.21022.8 ABCDEFGHI XYZ # Microsoft C/C++ 15.0.21022.8 (/Ox /Ot) ABCDEFGHI ZYX # Microsoft C/C++ 16.00.40219.01 ABCDEFGHI ZYX # cint C/C++ interpreter 5.16.19 ABCDEFGHI XYZ

24

/3 . 2012 . (3) /

DIABE TKWI W SZCZEGACH: C/C++, CZ 1

Oczywicie, w przypadku przykadowego kodu kolejno wywoywania funkcji nie ma adnego znaczenia. Mona jednak wyobrazi sobie przypadek, w ktrym poprawno dziaania polega na konkretnej kolejnoci ewaluacji wyrae taki kod przedstawiony jest na Listingu 2b. Podczas jego tworzenia, programista bdnie zaoy, e ewaluacja przy wywoaniu funkcji nastpuje zawsze od koca. Bd jest o tyle zoliwy, e prawdopodobnie program bdzie dziaa dobrze w rodowisku danego programisty. Niestety, zmiana flag kompilacji lub choby zmiana wersji kompilatora moe zmieni kolejno ewaluacji, co bdzie skutkowa uyciem niezainicjalizowanej zmiennej th, oraz (w optymistycznym przypadku) natychmiastowym crashem programu w momencie odczytu zmiennej. W przypadku pesymistycznym w zmiennej th znajdzie si warto wskazujca na istniejcy obszar pamici, a nowy wtek zacznie wykonywa losowy kawaek kodu, ktry ostatecznie i tak skoczy si wyjtkiem i zakoczeniem programu, ale bardzo utrudni znalezienie przyczyny. Mona wic wycign (poprawny) wniosek, e jzyki C oraz C++ nie definiuj dokadnej kolejnoci ewaluacji wyrae w niektrych przypadkach (warto zaznaczy, e wikszo wspczesnych jzykw dokadnie definiuje kolejno wykonania ewaluacji). Zamiast tego wprowadzone zostao pojcie punktw sekwencyjnych (ang. sequence points) s to jasno okrelone miejsca, po ktrych programista moe zaoy, e wszystkie operacje oraz ich efekty uboczne zostay ju wykonane (patrz ramka: Najwaniejsze punkty sekwencyjne w C/ C++). Takie podejcie pozwala kompilatorowi m.in. na optymalizacj poprzez dobranie najkorzystniejszej kolejnoci wykonywania dziaa i ewaluacji (jest to istotne w przypadku nowoczesnych architektur, na ktrych moliwe jest wykonanie kilku dobrze dobranych dziaa rwnoczenie). Wracajc do kodu tworzcego wtek aby uzyska poprawny program, wystarczy przenie wyraenie th = GetNewThreadStruct() przed punkt sekwencyjny poprzedzajcy ewaluacje argumentw funkcji SpawnThread, czyli np. do deklaracji zmiennej th (patrz Listing 2c). Listing 2c. Poprawiony kod tworzcy wtek
/* stwrz nowy wtek */ ThreadStruct *th = GetNewThreadStruct(); int tid = SpawnThread( th->ThreadFuncPtr, th );

Powyszy kod zawiera dwa punkty sekwencyjne: po inicjalizacji zmiennej a, oraz po drugim wyraeniu. Dodatkowo, standardy mwi, e przypisanie bdzie wykonane po obliczeniu wartoci obu stron (ale niekoniecznie po zaaplikowaniu efektw ubocznych, takich jak dodatkowe zapisy) [N1570 6.5.16] [N3337 5.17]. Drugie wyraenie zawiera natomiast stosunkowo duo operacji zapisu oraz odczytu wykonywanych na zmiennej a, a konkretniej: PPa++ jest operacj odczytu ze zmiennej a PPa++ jest rwnie operacj zapisu do zmiennej a PPanalogicznie, ++a jest operacj odczytu ze zmiennej a PP++a jest rwnie operacj zapisu do zmiennej a PPa = ... jest operacj zapisu do zmiennej a Poniewa w midzyczasie nie ma adnego punktu sekwencyjnego, powysze zapisy oraz odczyty mog wystpi w kilku rnych kolejnociach, co w ostatecznym rozrachunku sprowadza si do kilku rnych moliwych wynikw. Jaki jest wic poprawny wynik? Poprawnego wyniku niestety nie ma, poniewa w omawianych jzykach wyraenie, w ktrym wystpuje wicej ni jedna modyfikacja danej zmiennej (lub zapis i odczyt danej zmiennej bez ustalonej konkretnej kolejnoci, w jakiej zapis i odczyt si odbd), jest niezdefiniowanym zachowaniem (UB). Listing 3. W jakiej kolejnoci wykonaj si konstruktory?
/* plik klasa.h */ class MojaKlasa { public: MojaKlasa(); }; /* plik a.cpp */ MojaKlasa A; MojaKlasa B; /* plik b.cpp */ MojaKlasa C; MojaKlasa D;

Innym bardzo dobrym przykadem obrazujcym niejednoznacznoci wynikajce ze sformuowania kodu bez brania pod uwag punktw sekwencyjnych jest nastpujca zagadka [B1]:
int a = 5; a = a++ + ++a; /* Ile wynosi a? */

Ostatnim w tej czci przykadem niejasnej kolejnoci wykonania jest problem dynamicznej inicjalizacji zmiennych globalnych. Na Listingu 3 przedstawiony jest fragment przykadowego projektu, na ktry skadaj si trzy pliki: klasa.h zawierajca deklaracj klasy MojaKlasa, oraz pliki a.cpp oraz b.cpp deklarujce obiekty klasy MojaKlasa o nazwach A oraz B (a.cpp) i C oraz D (b.cpp). Powstaje pytanie: w jakiej kolejnoci zostan wywoane konstruktory obiektw A, B, C oraz D? Standard C++ wskazuje, e dynamiczna inicjalizacja nastpuje w kolejnoci deklaracji w danym pliku rdowym [N3337 3.6.2]. A wic w tym wypadku mamy pewno, e konstruktor obiektu A bdzie wywoany przed konstruktorem obiektu B, a konstruktor obiektu C bdzie wywoany przed konstruktorem obiektu D. Niestety, nadal daje to dwie moliwoci: A B C D oraz C D A B, tak wic mamy do czynienia z kolejnym zachowaniem typu unspecified behavior.
/www.programistamag.pl/

25

JZYKI PROGRAMOWANIA
W przypadkach, w ktrych zaley nam na konkretnej kolejnoci wywoywania konstruktorw, naley na przykad przepisa kod na dynamiczn alokacj obiektw w okrelonej przez nas kolejnoci. Dodatkowo niektre kompilatory, jak np. g++ (GCC), udostpniaj rozszerzenie jzyka, ktre pozwala okreli dokadn kolejno, w jakiej ma nastpi inicjalizacja obiektw globalnych wzgldem caego projektu suy do tego konstrukt __attribute__ ((init_priority(N)), gdzie N to priorytet inicjalizacji [9]. kilkugodzinn sesj z debuggerem, nieprzewidzianymi trudnociami przy przenoszeniu kodu, a czasem nawet skutecznym przeamaniem zabezpiecze aplikacji przez osoby trzecie. Jak wykaza powyszy tekst, tworzc kod w C czy C++, naley pamita o zakresie zmiennych (ktry czasem jest traktowany po macoszemu lub zupenie ignorowany), nie zawsze zdefiniowanej kolejnoci wykonywania kodu, a take o tym, e kod oparty o zachowanie niezdefiniowane nie zawsze musi pojawi si w ostatecznym kodzie wynikowym. W tym miejscu autor artykuu chciaby raz jeszcze zachci czytelnikw do przejrzenia standardw jzykw C oraz C++, a take do zapoznania si z zaleceniami na temat tworzenia bezpiecznego i stabilnego kodu, np. autorstwa CERT-u [7].

PODSUMOWANIE
I tak oto koczymy cz pierwsz artykuu o drobnych szczegach, ktrych pominicie moe skutkowa

Wszystkie opinie wyraone w artykule s prywatnymi opiniami autora.

W sieci
Najnowsze szkice standardw C i C++: PP [S1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/ n1570.pdf PP [S2] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf Dokumentacja popularnych kompilatorw: PP [D1] Free Software Fundation, Inc. GCC online documentation: http://gcc.gnu.org/onlinedocs/ PP [D2] Microsoft. MSDN, Visual C++: http://msdn.microsoft.com/en-us/library/60k1461a Pozostaa bibliografia: PP [1] Wikipedia. Przekroczenie zakresu liczb cakowitych: http://pl.wikipedia.org/wiki/Przekroczenie_zakresu_ liczb_ca%C5%82kowitych PP [2] Microsoft: http://msdn.microsoft.com/en-us/library/6ewkz 86d(v=vs.110).aspx PP [3] Matt Conover, w00w00 Security Team:

http://www.cgsecurity.org/exploit/heaptut.txt PP [4] Ben Hawkes, Attacking the Vista Heap: https://www.lateralsecurity.com/downloads/hawkes_ruxcon-nov-2008.pdf PP [5] MinGW GCC: http://www.mingw.org/ PP [6] Chris Lattner, What Every C Programmer Should Know About Undefined Behavior #1/3: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html PP [7] CERT. CERT Secure Coding Standards: https://www.securecoding.cert.org/confluence/display/ seccode/EXP10-C.+Do+not+depend+on+the+order+of+e valuation+of+subexpressions+or+the+order+in+which+si de+effects+take+place PP [8] Wikipedia. Sequence point: http://en.wikipedia.org/wiki/Sequence_point PP [9] Free Software Fundation, Inc. C++-Specific Variable, Function, and Type Attributes: http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes Materiay wasne: PP [B1] http://gynvael.coldwind.pl/?id=369

Gynvael Coldwind

gynvael@coldwind.pl

Na co dzie autor pracuje w firmie Google na stanowisku Information Security Engineer. Po godzinach prowadzi bloga oraz nagrywa podcasty o programowaniu (http://gynvael.coldwind. pl/). Hobbystycznie programuje od ponad 20 lat (w tym ponad 10 lat w C i C++).

26

/3 . 2012 . (3) /

You might also like