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.
komentarze obsługiwane przez Disqus