Skip to content

Commit 99472fc

Browse files
authored
Merge pull request #75687 from inogenous/segfault-repair-item-in-spillable-container
Prevent segfault when refitting item in spillable container
2 parents 454d585 + 7d59354 commit 99472fc

File tree

2 files changed

+87
-7
lines changed

2 files changed

+87
-7
lines changed

src/activity_handlers.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2482,6 +2482,11 @@ void repair_item_finish( player_activity *act, Character *you, bool no_menu )
24822482
const int old_level = you->get_skill_level( actor->used_skill );
24832483
const repair_item_actor::attempt_hint attempt = actor->repair( *you, *used_tool,
24842484
fix_location, repeat == repeat_type::REFIT_ONCE || repeat == repeat_type::REFIT_FULL );
2485+
// Warning: The above call to `repair_item_actor::repair` might
2486+
// invalidate the item and the item_location, for example when
2487+
// spilling items from spillable containers. It is therefore
2488+
// important that we don't use `fix_location` in code below
2489+
// here without first checking whether it is still valid.
24852490

24862491
// If the item being repaired has been destroyed stop further
24872492
// processing in case the items being used for the repair was
@@ -2512,6 +2517,7 @@ void repair_item_finish( player_activity *act, Character *you, bool no_menu )
25122517
// But only if we didn't destroy the item (because then it's obvious)
25132518
const bool destroyed = attempt == repair_item_actor::AS_DESTROYED;
25142519
const bool cannot_continue_repair = attempt == repair_item_actor::AS_CANT || destroyed ||
2520+
!fix_location ||
25152521
!actor->can_repair_target( *you, *fix_location, !destroyed, true );
25162522
if( cannot_continue_repair ) {
25172523
// Cannot continue to repair target, select another target.

tests/degradation_test.cpp

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "activity_handlers.h"
44
#include "cata_catch.h"
5+
#include "flag.h"
56
#include "item.h"
67
#include "itype.h"
78
#include "iuse_actor.h"
@@ -17,8 +18,12 @@
1718

1819
static const activity_id ACT_REPAIR_ITEM( "ACT_REPAIR_ITEM" );
1920

21+
static const itype_id itype_backpack( "backpack" );
22+
static const itype_id itype_can_drink( "can_drink" );
23+
static const itype_id itype_canvas_patch( "canvas_patch" );
2024
static const itype_id itype_leather( "leather" );
2125
static const itype_id itype_tailors_kit( "tailors_kit" );
26+
static const itype_id itype_technician_shirt_gray( "technician_shirt_gray" );
2227
static const itype_id itype_test_baseball( "test_baseball" );
2328
static const itype_id itype_test_baseball_half_degradation( "test_baseball_half_degradation" );
2429
static const itype_id itype_test_baseball_x2_degradation( "test_baseball_x2_degradation" );
@@ -221,22 +226,28 @@ TEST_CASE( "Items_that_get_damaged_gain_degradation", "[item][degradation]" )
221226
}
222227
}
223228

224-
static void setup_repair( item &fix, player_activity &act, Character &u )
229+
static item_location setup_tailorkit( Character &u )
225230
{
226231
map &m = get_map();
232+
const tripoint_bub_ms pos = spawn_pos;
233+
item &thread = m.add_item_or_charges( pos, item( itype_thread ) );
234+
item &tailor = m.add_item_or_charges( pos, item( itype_tailors_kit ) );
235+
thread.charges = 400;
236+
tailor.reload( u, { map_cursor( pos ), &thread }, 400 );
237+
REQUIRE( m.i_at( spawn_pos ).begin()->typeId() == tailor.typeId() );
238+
return item_location( map_cursor( pos ), &tailor );
239+
}
227240

241+
static void setup_repair( item &fix, player_activity &act, Character &u )
242+
{
228243
// Setup character
229244
clear_character( u, true );
230245
u.set_skill_level( skill_tailor, 10 );
231246
u.wield( fix );
232247
REQUIRE( u.get_wielded_item()->typeId() == fix.typeId() );
233248

234249
// Setup tool
235-
item &thread = m.add_item_or_charges( spawn_pos, item( itype_thread ) );
236-
item &tailor = m.add_item_or_charges( spawn_pos, item( itype_tailors_kit ) );
237-
thread.charges = 400;
238-
tailor.reload( u, { map_cursor( tripoint_bub_ms( spawn_pos ) ), &thread }, 400 );
239-
REQUIRE( m.i_at( spawn_pos ).begin()->typeId() == tailor.typeId() );
250+
item_location tailorloc = setup_tailorkit( u );
240251

241252
// Setup materials
242253
item leather( itype_leather );
@@ -245,7 +256,6 @@ static void setup_repair( item &fix, player_activity &act, Character &u )
245256

246257
// Setup activity
247258
item_location fixloc( u, &fix );
248-
item_location tailorloc( map_cursor( tripoint_bub_ms( spawn_pos ) ), &tailor );
249259
act.values.emplace_back( /* repeat_type::FULL */ 3 );
250260
act.str_values.emplace_back( "repair_fabric" );
251261
act.targets.emplace_back( tailorloc );
@@ -628,3 +638,67 @@ TEST_CASE( "Gun_repair_with_degradation", "[item][degradation]" )
628638
}
629639
}
630640
}
641+
642+
static player_activity setup_repair_activity( item_location &tailorloc, item_location &fixloc )
643+
{
644+
player_activity act( ACT_REPAIR_ITEM );
645+
act.values.emplace_back( /* repeat_type::FULL */ 3 );
646+
act.str_values.emplace_back( "repair_fabric" );
647+
act.targets.emplace_back( tailorloc );
648+
act.targets.emplace_back( fixloc );
649+
return act;
650+
}
651+
652+
static item_location put_in_container( item_location &container, const itype_id &type )
653+
{
654+
ret_val<item *> inserted = container->get_contents().insert_item( item( type ),
655+
pocket_type::CONTAINER );
656+
REQUIRE( inserted.success() );
657+
return item_location( container, inserted.value() );
658+
}
659+
660+
// Reproduce previous segfault from https://github.com/CleverRaven/Cataclysm-DDA/issues/74254
661+
TEST_CASE( "refit_item_inside_spillable_container", "[item][repair][container]" )
662+
{
663+
clear_avatar();
664+
clear_map();
665+
set_time_to_day();
666+
REQUIRE( static_cast<int>( get_map().light_at( spawn_pos.raw() ) ) > 2 );
667+
668+
Character &u = get_player_character();
669+
u.set_skill_level( skill_tailor, 10 );
670+
671+
// Setup starting equipment
672+
item_location tailorkit = setup_tailorkit( u );
673+
REQUIRE( u.wear_item( item( itype_backpack ) ) );
674+
item_location backpack = u.top_items_loc().front();
675+
item canvas_patch( itype_canvas_patch );
676+
u.i_add_or_drop( canvas_patch, 100 );
677+
678+
// Starting inventory looks like:
679+
// backpack >
680+
// 100 canvas patch
681+
// aluminum can >
682+
// work t-shirt (poor fit)
683+
// It's a bit odd that we can put the shirt into the aluminum can, since it
684+
// would normally reject that with a message stating that it would spill.
685+
GIVEN( "backpack > aluminum can > work t-shirt (poor fit)" ) {
686+
item_location aluminum_can = put_in_container( backpack, itype_can_drink );
687+
item_location fix_tshirt = put_in_container( aluminum_can, itype_technician_shirt_gray );
688+
WHEN( "Refitting tshirt inside spillable container" ) {
689+
REQUIRE_FALSE( fix_tshirt->has_flag( flag_FIT ) );
690+
player_activity act = setup_repair_activity( tailorkit, fix_tshirt );
691+
while( !act.is_null() ) {
692+
::repair_item_finish( &act, &u, true );
693+
}
694+
THEN( "tshirt should be refitted successfully" ) {
695+
const item *refitted_tshirt = backpack->get_item_with( [&]( const item & i ) {
696+
return i.typeId() == itype_technician_shirt_gray;
697+
} );
698+
REQUIRE( refitted_tshirt != nullptr );
699+
CHECK( refitted_tshirt->has_flag( flag_FIT ) );
700+
}
701+
}
702+
703+
}
704+
}

0 commit comments

Comments
 (0)