5 lutego 2020

[C++] problemy z rzutowaniem

Chociaż static_cast i dynamic_cast (i inne) w C++ miały być lepszą wersją "surowego" rzutowania znanego z języka C, nie rozwiązały wszystkich problemów. dynamic_cast można stosować tylko tam, gdzie mamy do czynienia z polimorfizmem, w dodatku angażując to RTTI (Run Time Type Information), które może zaważyć na czasie wykonania programu. static_cast z kolei, posiada niezdefiniowane zachowanie jeżeli próbujemy rzutować obiekt w dół hierarchii.

W przykładzie poniżej kompilator nie zaprotestuje, gdy zrzutujemy obiekt klasy Base na klasę Derived. Problem w tym, że obiekt base rezerwuje mniej pamięci (nie ma pola value_b) niż obiekt derived. W konsekwencji, pisane do pola value_b, będzie skutkowało pisaniem po pamięci.
#include <iostream>

using namespace std;

struct Base {
    void fun() { printf("Base method\n"); }
    int value_a;
};

struct Derived : public Base {
    void fun() { printf("Derived method\n"); }
    int value_b;
};


int main() {
    Base* base = new Base{};
    Derived* derived = static_cast<Derived*>(base); // no-error!

    derived->value_a = 1;
    derived->value_b = 2;       // pisanie po pamięci!

    cout << derived->value_a << endl;
    cout << derived->value_b << endl;
}
Tutaj dopisało nam szczęści, program wykonał się prawidłowo.
$ clang++ -std=c++17  main.cpp
$ ./a.out 
1
2
Sprawa wygląda inaczej, gdy dołączymy address sanitizer.
$ clang++ -std=c++17 -fsanitize=address main.cpp
$ ./a.out
==23018==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000014 at pc 0x0000004c63ba bp 0x7fff3301c9b0 sp 0x7fff3301c9a8
WRITE of size 4 at 0x602000000014 thread T0
...

Sanitizer potrafi wykryć tego typu problem, tylko gdy zaczynamy pisać po nie swojej pamięci. Naukowcy: Byoungyoung Lee, Chengyu Song, Taesoo Kim i Wenke Lee z Georgia Institute of Technology, stworzyli jeszcze inne narzędzie do detekcji tego typu problemów. Oparte na LLVM, dodaje do każdego static_cast tablicę z informacjami o hierarchii dziedziczenia (czyli coś w rodzaju tego co posiada dynamic_cast) i w zgrabny sposób informuje gdzie obiekt został stworzony i gdzie źle rzutowany, gdy tylko takie rzutowanie nastąpi. Za swoją pracę zostali nagrodzeni przez Facebooka fajną nagrodą pieniężną.

Brak komentarzy:

Prześlij komentarz