CleverRaven / Cataclysm-DDA

Cataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.
http://cataclysmdda.org
Other
10.11k stars 4.11k forks source link

Crash. #29043

Closed Necrosian closed 5 years ago

Necrosian commented 5 years ago

Describe the bug
Had multiple crashes while looting a city

To Reproduce
I have no idea was walking/looting when last crash happened.

Expected behavior
Just crashes the game.

Screenshots
crash

Versions and configuration(please complete the following information):

Additional context
Crash Log File:

CRASH LOG FILE: config/crash.log VERSION: 0.D-1042-ge3964f4 TYPE: Signal MESSAGE: SIGSEGV: Segmentation fault STACK TRACE: @0x555FB5[cataclysm-tiles.exe+0x155FB5] @0x556B62[cataclysm-tiles.exe+0x156B62] SMPEG_error+0x4B034@0xE280A0[cataclysm-tiles.exe+0xA280A0] _C_specific_handler+0x98@0x7FFAB31E8068[msvcrt.dll+0x28068] _chkstk+0x11F@0x7FFAB47F468F[ntdll.dll+0xA468F] RtlWalkFrameChain+0x14BF@0x7FFAB4754BEF[ntdll.dll+0x4BEF] KiUserExceptionDispatcher+0x2E@0x7FFAB47F33FE[ntdll.dll+0xA33FE] @0x60ACB3[cataclysm-tiles.exe+0x20ACB3] @0x60BB78[cataclysm-tiles.exe+0x20BB78] @0x611300[cataclysm-tiles.exe+0x211300] @0x618127[cataclysm-tiles.exe+0x218127] @0x682F4E[cataclysm-tiles.exe+0x282F4E] IMG_LoadWEBP_RW+0x4D91C9@0x13F30D9[cataclysm-tiles.exe+0xFF30D9] @0x4013ED[cataclysm-tiles.exe+0x13ED] @0x4014FB[cataclysm-tiles.exe+0x14FB] BaseThreadInitThunk+0x14@0x7FFAB1B581F4[KERNEL32.DLL+0x181F4] RtlUserThreadStart+0x21@0x7FFAB47BA251[ntdll.dll+0x6A251]

Edit: Uploaded a save zip.zip

neitsa commented 5 years ago

Could you please upload your save file? It would definitely help to understand what's going on. Thanks!

Did you do anything special when the crash happened? Anything you could think of could help to repro the bug and fix it.

ZikoWong commented 5 years ago

你能上传你的保存文件吗?这肯定有助于了解正在发生的事情。谢谢!

事故发生时你做了什么特别的事吗?你能想到的任何东西都可以帮助重现bug并修复它。

this mistake is almost identical to what I have encountered. It should be ascend stairs. Part of the ascend stairs in the city makes this mistake. I can't tell where it will be, but I know.research facility are one of them. will be a crash

My English is not good. I hope you can understand it.

Necrosian commented 5 years ago

Could you please upload your save file? It would definitely help to understand what's going on. Thanks!

Did you do anything special when the crash happened? Anything you could think of could help to repro the bug and fix it.

Had several crashes before in couple minute intervals. Doing random things(looting, exploring, fighting) and it just crashed.

neitsa commented 5 years ago

@Necrosian Many thanks :+1: I'll try to reproduce the bug on my side and keep you updated.

@ZikoWong Understood :+1: This could definitely help to track the bug down.

neitsa commented 5 years ago

I was able to trigger the big on my side. With ZhilkinSerg's help I could confirm it's the same bug.

Sorry for the wall of text that follows.

Here'a a complete stack trace (see on the right for function names and source code files):

0:000> kb
 # RetAddr           : Args to Child                                                           : Call Site
00 00007ff6`c97f2bab : 00000023`313ac638 ffffffff`ffffffff 00000023`313ac5e8 00000023`313adb38 : Cataclysm!std::vector<maptile,std::allocator<maptile> >::operator[]+0x87 [d:\programming\microsoft visual studio 14.0\vc\include\vector @ 1235] 
01 00007ff6`c97db8b6 : 00000023`313adab0 00000154`339c0a40 00000023`313adb38 00000000`0000002b : Cataclysm!<lambda_d68268ff737706d09db60fb4644b745b>::operator()+0x7bb [k:\cdda\cataclysm-dda\src\field.cpp @ 718] 
02 00007ff6`c97d7e66 : 00000153`e77f69d0 00000154`0921c200 00000023`00000000 00007ff6`00000000 : Cataclysm!map::process_fields_in_submap+0x39f6 [k:\cdda\cataclysm-dda\src\field.cpp @ 1447] 
03 00007ff6`c9819d4a : 00000153`e77f69d0 00000023`313ae7a8 00000023`313ae750 00000001`00000036 : Cataclysm!map::process_fields+0x146 [k:\cdda\cataclysm-dda\src\field.cpp @ 523] 
04 00007ff6`c8ecb83a : 00000153`e77d6e30 00000000`00000338 00000000`0000003f 00000000`00000001 : Cataclysm!game::do_turn+0xbca [k:\cdda\cataclysm-dda\src\game.cpp @ 1462] 
05 00007ff6`c8e301d2 : 00000153`00000000 00000153`e5ac0008 00000153`e3d92fcc 00000401`00000000 : Cataclysm!SDL_main+0x16fa [k:\cdda\cataclysm-dda\src\main.cpp @ 682] 
06 00007ff6`cb1587fd : 00000000`00000000 00000000`00000000 00000000`00000000 00007ff6`cb57fe80 : Cataclysm!main_getcmdline+0xf2 [c:\projects\sdl\src\main\windows\sdl_windows_main.c @ 177] 
07 00007ff6`cb1586c7 : 00007ff6`cb578000 00007ff6`cb57f930 00000000`00000000 00000000`00000000 : Cataclysm!invoke_main+0x2d [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 99] 
08 00007ff6`cb15858e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : Cataclysm!__scrt_common_main_seh+0x127 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 253] 
09 00007ff6`cb158819 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : Cataclysm!__scrt_common_main+0xe [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 296] 
0a 00007ffc`a85c81f4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : Cataclysm!WinMainCRTStartup+0x9 [f:\dd\vctools\crt\vcstartup\src\startup\exe_winmain.cpp @ 17] 
0b 00007ffc`a9fca251 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
0c 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

This happens in the map::process_fields_in_submap function, exactly here

    end_it = static_cast<size_t>( rng( 0, neighs.size() - 1 ) );
    for( size_t i = ( end_it + 1 ) % neighs.size() ;
         // Start at end_it + 1, then wrap around until i == end_it
         i != end_it;
         i = ( i + 1 ) % neighs.size() ) {
        const auto &neigh = neighs[i];
        if( ( neigh.x != remove_tile.x && neigh.y != remove_tile.y ) ||
            ( neigh.x != remove_tile2.x && neigh.y != remove_tile2.y ) ||
            ( neigh.x != remove_tile3.x && neigh.y != remove_tile3.y ) ) {
            neighbour_vec.push_back( neigh );
        }
    }
    spread_to( neighbour_vec[ rng( 0, neighbour_vec.size() - 1 ) ] ); // Crash here

So, neighbour_vec is indexed but it's empty which leads to a crash.

The state of the local variables at time of crash:

0:000> dx Debugger.Sessions[0].Processes[22672].Threads[44860].Stack.Frames[1].SwitchTo();dv /t /v
Debugger.Sessions[0].Processes[22672].Threads[44860].Stack.Frames[1].SwitchTo()
00000023`313ac880 class map::process_fields_in_submap::__l2::<lambda_d68268ff737706d09db60fb4644b745b> * this = 0x00000023`313adab0
00000023`313ac888 class field_entry * cur = 0x00000154`339c0a40
00000023`313ac890 struct tripoint * p = 0x00000023`313adb38
00000023`313ac898 field_id curtype = fd_hot_air3 (0n43)
00000023`313ac8a0 int percent_spread = 0n100
00000023`313ac8a8 class time_duration * outdoor_age_speedup = 0x00000023`313acd9c
00000023`313ac530 unsigned int64 end_it = 7
00000023`313ac618 class std::vector<unsigned __int64,std::allocator<unsigned __int64> > spread = { size=0 }
00000023`313ac6d8 struct maptile remove_tile3 = struct maptile
00000023`313ac670 class map::process_fields_in_submap::__l2::<lambda_d68268ff737706d09db60fb4644b745b>::()::__l2::<lambda_c8a944397b3f668861a82793f7b7af54> spread_to = class map::process_fields_in_submap::__l2::<lambda_d68268ff737706d09db60fb4644b745b>::()::__l2::<lambda_c8a944397b3f668861a82793f7b7af54>
00000023`313ac520 bool sheltered = false
00000023`313ac5f0 class map::process_fields_in_submap::__l2::<lambda_d68268ff737706d09db60fb4644b745b>::()::__l2::<lambda_c4266a27ca14e405effa26e88c337faa> can_spread_to = class map::process_fields_in_submap::__l2::<lambda_d68268ff737706d09db60fb4644b745b>::()::__l2::<lambda_c4266a27ca14e405effa26e88c337faa>
00000023`313ac5e8 int winddirection = 0n270
00000023`313ac538 int windpower = 0n9
00000023`313ac588 class int_id<oter_t> * cur_om_ter = 0x00000154`00197950
00000023`313ac790 class std::array<maptile,8> neighs = { size=8 }
00000023`313ac5e0 int current_density = 0n3
00000023`313ac638 class std::vector<maptile,std::allocator<maptile> > neighbour_vec = { size=0 }
00000023`313ac6c0 struct maptile remove_tile2 = struct maptile
00000023`313ac740 class std::tuple<maptile,maptile,maptile> maptiles = class std::tuple<maptile,maptile,maptile>
00000023`313ac5e4 class time_duration current_age = class time_duration
00000023`313ac6a8 struct maptile remove_tile = struct maptile

The neigh array has 8 elements:

0:000> dx -r1 (*((Cataclysm!std::array<maptile,8> *)0x23313ac790))
(*((Cataclysm!std::array<maptile,8> *)0x23313ac790))                 : { size=8 } [Type: std::array<maptile,8>]
    [<Raw View>]     [Type: std::array<maptile,8>]
    [0]              [Type: maptile]
    [1]              [Type: maptile]
    [2]              [Type: maptile]
    [3]              [Type: maptile]
    [4]              [Type: maptile]
    [5]              [Type: maptile]
    [6]              [Type: maptile]
    [7]              [Type: maptile]

Below is the state of each of them (we are only interested on their x and ymembers):

0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac790))                       // index 0
(*((Cataclysm!maptile *)0x23313ac790))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac7a8))                       // index 1
(*((Cataclysm!maptile *)0x23313ac7a8))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac7c0))                       // index 2
(*((Cataclysm!maptile *)0x23313ac7c0))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac7d8))                       // index 3
(*((Cataclysm!maptile *)0x23313ac7d8))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac7f0))                       // index 4
(*((Cataclysm!maptile *)0x23313ac7f0))                 [Type: maptile]
    [+0x000] sm               : 0x1540921c200 [Type: submap *]
    [+0x008] x                : 0x1 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac808))                       // index 5
(*((Cataclysm!maptile *)0x23313ac808))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac820))                       // index 6
(*((Cataclysm!maptile *)0x23313ac820))                 [Type: maptile]
    [+0x000] sm               : 0x1540921c200 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x1 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac838))                       // index 7
(*((Cataclysm!maptile *)0x23313ac838))                 [Type: maptile]
    [+0x000] sm               : 0x1540921c200 [Type: submap *]
    [+0x008] x                : 0x1 [Type: unsigned __int64]
    [+0x010] y                : 0x1 [Type: unsigned __int64]

Given this comparison:

        if( ( neigh.x != remove_tile.x && neigh.y != remove_tile.y ) ||
            ( neigh.x != remove_tile2.x && neigh.y != remove_tile2.y ) ||
            ( neigh.x != remove_tile3.x && neigh.y != remove_tile3.y ) ) {
            neighbour_vec.push_back( neigh );
        }

And the state of remove_tile, remove_tile2 and remove_tile3:

0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac6a8))                   // remove_tile
(*((Cataclysm!maptile *)0x23313ac6a8))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac6c0))                     // remove_tile2
(*((Cataclysm!maptile *)0x23313ac6c0))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]
0:000> dx -r1 (*((Cataclysm!maptile *)0x23313ac6d8))                      // remove_tile3
(*((Cataclysm!maptile *)0x23313ac6d8))                 [Type: maptile]
    [+0x000] sm               : 0x7ff6cc197590 [Type: submap *]
    [+0x008] x                : 0x0 [Type: unsigned __int64]
    [+0x010] y                : 0x0 [Type: unsigned __int64]

The comparison in the if should match the last tile index 7 (which has x = 1 and y = 1) and this tile should be pushed to the vector.

Edit

Now with this code for the processing loop:

    for( size_t i = ( end_it + 1 ) % neighs.size() ;
         i != end_it; 
         i = ( i + 1 ) % neighs.size() )

Proposed patch: just check that the vector is not empty before indexing it.