2017-06-29 50 views
17

Poniższy problem został poddany destylacji z ogromnego projektu i najbardziej minimalnego przykładu problemu, jaki udało mi się wymyślić.Szukam wyjaśnienia błędu korupcji stosu

Wiem, że wywodzenie się z std::string jest złe i zostało już zmienione w naszej bazie kodu, ale próbuję zrozumieć, co dzieje się pod maską tutaj.

Kod awarii na Visual C++ 2017

Microsoft Visual Studio Community 2017 
Version 15.2 (26430.14) Release 
Visual C++ 2017 00369-60000-00001-AA257 

w trybie tylko uwalniania (z optymalizacją prędkości). Nie ulega awarii w trybie zwolnienia bez optymalizacji prędkości.

#include <string> 
#include <string_view> 
#include <vector> 

struct my_string : public std::string 
{ 
    __declspec(noinline) 
     my_string::my_string(const std::string_view& str) : 
     std::string(str.data(), str.size()) 
    {} 

    template <typename T> 
    my_string& arg(T) 
    { 
     return *this; 
    } 
}; 

struct my_string_view : public std::string_view 
{ 
    my_string_view(const std::string_view::value_type* val) : 
     std::string_view(val) {} 

    template <typename... PARAMS> 
    my_string arg(PARAMS&&... prms) { 
     return my_string(*this).arg(std::forward<PARAMS>(prms)...); 
    } 
}; 

template <typename T> 
struct basic_color 
{ 
    T r, g, b, a; 

    basic_color() : r(0), g(0), b(0), a(255) {} 

    template <typename U> 
    explicit basic_color(const basic_color<U>& c) : 
     r(c.r), g(c.g), b(c.b), a(c.a) 
    {} 
}; 

using color = basic_color<std::uint8_t>; 
using float_color = basic_color<float>; 

__declspec(noinline) 
void change_float_color(float_color& color) 
{ 
    color.r = 0.1f; 
} 

int main() 
{ 
    std::vector<float_color> colors = { {} }; 
    float sum = 0; 
    for (std::uint32_t i = 0; i < 1; ++i) 
    { 
     float_color fc; 
     change_float_color(fc); 
     color c(fc); 
     std::vector<std::string> msgs; 
     msgs.push_back(my_string_view("").arg(c.r)); 
     msgs.push_back(my_string_view("").arg(c.g)); 
     sum += fc.b - colors[i].b; 
    } 
    return static_cast<int>(sqrt(sum)); 
} 

Błąd w Visual Studio jest to (spojrzeć na połamanych rozmiarach msgs i colors na dole):

enter image description here

Domyślam się, że połączenie z std::vector<std::string>::push_back(std::string&&) z my_string jest problematyczne (zachowanie przypominające cięcie). Ale w jaki sposób może to zepsuć stos (lub wskaźnik stosu)?

Czy ktoś ma pojęcie, co może się tutaj wydarzyć lub jak się dowiedzieć?

Here to mój projekt na wypadek, gdyby ktoś chciał odtworzyć problem.

+4

Użyj debuggera, punktem przełomowym jest tutaj broń. –

+0

"ponieważ std :: string nie ma wirtualnego destruktora, wartości r podane do push_back są pocięte na plasterki." Cięcie nie ma nic wspólnego z wirtualnym destruktorem. Wartości zostaną podzielone niezależnie. "W ten sposób destruktor klasy pochodnej (my_string) nigdy nie jest wywoływany, część śmieci pozostaje na stosie" To nie ma żadnego sensu. 'sizeof (my_string) == sizeof (std :: string)' Nie robi to żadnej różnicy. "spójrz na zepsute rozmiary" Kompilator może swobodnie robić, co chce, z tymi obiektami, ponieważ nigdy nie zostaną użyte ponownie. –

+0

> Moim pierwszym przypuszczeniem było to, że ponieważ std :: string nie ma wirtualnego destruktora, wartości r podane dla parametru push_back są podzielone na kawałki. Zatem destruktor klasy pochodnej (my_string) nigdy nie jest nazywany Hedede

Odpowiedz

4

Myślę, że jest to błąd kompilatora.

Oto, co widzę po demontażu: po wpisie main() zapisano esp w ebx. Na koniec esp jest przywracane z ebx. Jednak w środku (po wywołaniu std::_Destroy_range1) wartość jest nadpisywana przez coś innego. Na koniec instrukcja ret używa fałszywej wartości esp i przeskakuje do niewłaściwego miejsca.

Rzeczywiście, stos nie jest uszkodzony (ten błąd nie może zostać przechwycony z punktami przerwania danych, jak zasugerował Hans), ale wskaźnik stosu jest.

+0

Dziękuję za odtworzenie problemu. Mam nadzieję [to] (https://social.msdn.microsoft.com/Forums/vstudio/en-US/b40ebfa9-9fdb-44f9-a778-6c7e24e1c2e4/stack-corrupting-due-to-a-bug-in- the-compileroptimizer? forum = vcgeneral) było poprawnym miejscem w MSDN, aby opublikować problem. W każdym razie sprawdzę, czy problem zniknął z oficjalną wersją następnej aktualizacji kompilatora. –

+0

@Tobias: [vcblog] (https://blogs.msdn.microsoft.com/vcblog/) sugeruje zgłaszanie błędów za pomocą strony [Connect] (https://connect.microsoft.com/VisualStudio). Zgadzam się z twoim komentarzem: ten błąd jest niestabilny, możemy być pewni, że zostanie on naprawiony, jeśli programista VS powie "tak, przeanalizowałem problem i naprawiłem go" – geza

+0

OK, [gotowe] (https: // connect .microsoft.com/VisualStudio/Feedback/Details/3136753). Dzięki jeszcze raz. –