Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite loop and RAM exhaustion when attempting to split vehicle #3552

Closed
olanti-p opened this issue Nov 4, 2023 · 1 comment · Fixed by #3559
Closed

Infinite loop and RAM exhaustion when attempting to split vehicle #3552

olanti-p opened this issue Nov 4, 2023 · 1 comment · Fixed by #3559
Labels
bug crash Game crashes or softlocks
Milestone

Comments

@olanti-p
Copy link
Contributor

olanti-p commented Nov 4, 2023

Describe the bug

Infinite loop with RAM exhaustion happens when attempting to split vehicle.

Steps To Reproduce

  1. Have a task manager or a debugger on standby to quickly stop the process
  2. Load the attached save: cbn-stack-exhaust-veh-split.zip OR do step 3
  3. Spawn a car, wield a crowbar, give yourself 200 strength and stand on the car's right side in the middle
  4. Keep smashing to the left until the car splits into 2
  5. Instead of splitting, the game will hang and RAM usage will skyrocket

Screenshots

No response

Versions and configuration

Game version eb8ffb2

Additional context

Related: #2250.

Found while testing #3550, but is not affected by that PR.

LLDB backtrace when the game was paused while in the loop

frame #0: 0x00007ffff79e6cca libc.so.6`__memcpy_avx_unaligned_erms at memmove-vec-unaligned-erms.S:708
frame #1: 0x0000555556455535 cataclysm-tiles`location_vector<item>::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] item** std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<item*>(__first=<unavailable>, __last=<unavailable>, __result=0x00005555e74831a0) at stl_algobase.h:431:6
frame #2: 0x000055555645552a cataclysm-tiles`location_vector<item>::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] item** std::__copy_move_a2<true, item**, item**>(__first=<unavailable>, __last=<unavailable>, __result=0x00005555e74831a0) at stl_algobase.h:494:14
frame #3: 0x000055555645552a cataclysm-tiles`location_vector<item>::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] item** std::__copy_move_a1<true, item**, item**>(__first=<unavailable>, __last=<unavailable>, __result=0x00005555e74831a0) at stl_algobase.h:522:14
frame #4: 0x000055555645552a cataclysm-tiles`location_vector<item>::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] __gnu_cxx::__normal_iterator<item**, std::vector<item*, std::allocator<item*>>> std::__copy_move_a<true, __gnu_cxx::__normal_iterator<item**, std::vector<item*, std::allocator<item*>>>, __gnu_cxx::__normal_iterator<item**, std::vector<item*, std::allocator<item*>>>>(__first=<unavailable>, __last=<unavailable>, __result=__normal_iterator<item **, std::vector<item *, std::allocator<item *> > > @ scalar) at stl_algobase.h:530:3
frame #5: 0x000055555645552a cataclysm-tiles`location_vector<item>::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] __gnu_cxx::__normal_iterator<item**, std::vector<item*, std::allocator<item*>>> std::move<__gnu_cxx::__normal_iterator<item**, std::vector<item*, std::allocator<item*>>>, __gnu_cxx::__normal_iterator<item**, std::vector<item*, std::allocator<item*>>>>(__first=<unavailable>, __last=<unavailable>, __result=__normal_iterator<item **, std::vector<item *, std::allocator<item *> > > @ scalar) at stl_algobase.h:652:14
frame #6: 0x000055555645552a cataclysm-tiles`location_vector<item>::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] std::vector<item*, std::allocator<item*>>::_M_erase(this=<unavailable>, __position=std::vector<item *, std::allocator<item *> >::iterator @ scalar) at vector.tcc:175:2
frame #7: 0x000055555645551c cataclysm-tiles`location_vector<item>::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] std::vector<item*, std::allocator<item*>>::erase(this=<unavailable>, __position=std::vector<item *, std::allocator<item *> >::const_iterator @ scalar) at stl_vector.h:1431:16
frame #8: 0x000055555645551c cataclysm-tiles`location_vector<item>::erase(this=0x000055556b2f5090, it=<unavailable>, out=0x0000000000000000) at location_vector.cpp:304:56
frame #9: 0x0000555556b6ced3 cataclysm-tiles`vehicle_stack::erase(location_vector<item>::const_iterator, detached_ptr<item>*) [inlined] vehicle::remove_item(this=0x000055556b1d6dd0, part=<unavailable>, it=item_stack::const_iterator @ 0x00007fffffffcf90, ret=<unavailable>) at vehicle.cpp:5507:54
frame #10: 0x0000555556b6ce86 cataclysm-tiles`vehicle_stack::erase(this=0x00007fffffffd000, it=item_stack::const_iterator @ 0x00007fffffffd028, out=0x0000000000000000) at vehicle.cpp:235:22
frame #11: 0x0000555556b816e2 cataclysm-tiles`vehicle::part_removal_cleanup(this=0x000055556b1d6dd0) at vehicle.cpp:1970:23
frame #12: 0x00005555564b6f6e cataclysm-tiles`map::vehmove(this=0x0000555558124e10) at map.cpp:507:19
frame #13: 0x00005555560a4d59 cataclysm-tiles`game::do_turn(this=0x000055555762e780) at game.cpp:1502:7
frame #14: 0x0000555556c8c93c cataclysm-tiles`main(argc=<unavailable>, argv=<unavailable>) at main.cpp:766:20
frame #15: 0x00007ffff786fd90 libc.so.6`__libc_start_call_main(main=(cataclysm-tiles`main at main.cpp:191), argc=1, argv=0x00007fffffffda88) at libc_start_call_main.h:58:16
frame #16: 0x00007ffff786fe40 libc.so.6`__libc_start_main_impl(main=(cataclysm-tiles`main at main.cpp:191), argc=1, argv=0x00007fffffffda88, init=<unavailable>, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffda78) at libc-start.c:392:3
frame #17: 0x00005555557ba9e5 cataclysm-tiles`_start + 37
@olanti-p
Copy link
Contributor Author

olanti-p commented Nov 4, 2023

Looks like when the vehicle parts are erased by it = parts.erase( it ) here

Cataclysm-BN/src/vehicle.cpp

Lines 1966 to 1979 in d82fd66

for( std::vector<vehicle_part>::iterator it = parts.begin(); it != parts.end(); /* noop */ ) {
if( it->removed ) {
auto items = get_items( std::distance( parts.begin(), it ) );
while( !items.empty() ) {
items.erase( items.begin() );
}
const tripoint &pt = global_part_pos3( *it );
here.clear_vehicle_point_from_cache( this, pt );
it = parts.erase( it );
changed = true;
} else {
++it;
}
}

parts, which is of type std::vector<vehicle_part>, reallocates the vehicle parts by copying them like this:

vehicle_part &vehicle_part::operator=( const vehicle_part &source )
{
copy_static_from( source );
base = item::spawn( *source.base );
for( item * const &it : source.items ) {
items.push_back( item::spawn( *it ) );
}
return *this;
}

Which ends up spawning extra items.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug crash Game crashes or softlocks
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants