Ostrożnie z c_str na obiekcie tymczasowym
Popatrzmy na następujący fragment kodu (fragment prostego parsera napisu zawierającego datę)
char *end; tm br; br.tm_year = strtol(dateTime.substr(0, 4).c_str(), &end, 10) - 1900; if(*end != 0) return false;
Intencją Autora było potraktowanie pierwszych czterech znaków jako liczby (chodziło o rok) - i zwrócenie false jeśli coś jest niedobrze.
Kod niby działał aż tu nagle w programie wielowątkowym zaczął od czasu do czasu irracjonalnie zwracać false na jak najbardziej poprawnych liczbach.
Co się stało? Ano:
- pisząc dateTime.substr(0,4) tworzymy obiekt tymczasowy (string), który alokuje sobie troszkę pamięci na stercie;
- przekazujemy do strtol tak naprawdę właśnie adres tego co sobie ten tymczasowy obiekt zaalokował na stercie (wydłubany przy pomocy c_str);
- strtol przypisuje przekazanej zmiennej (end) adres pierwszego nie-sparsowanego znaku (jest to adres końcowego zera jeśli cały tekst udało się nam sparsować a adres pierwszego kłopotliwego znaku w innej sytuacji);
- gdy kończymy obliczenie strtol obiekt tymczasowy jest zwalniany a pamięć wraca na stertę;
- innymi słowy, gdy robimy if(*end != 0) end wskazuje na pamięć która jest już przez nikogo nie zaalokowana;
- w programie jednowątkowym prawdopodobnie to przeżyjemy a w wielowątkowym może się zdarzyć, że w międzyczasie inny wątek nam podkradnie tę pamięć i ją nadmaże - i test nie będzie już dobry;
- to się naprawdę zdarzyło.
- «Co ma wspólnego Oracle z numeric_limits
- String i vector nie chcą oddać pamięci»
- ↑C++ - sztuczki i niebezpieczeństwa