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

Fix inventory caching bug causing crash when resuming craft #36762

Merged
merged 1 commit into from
Jan 7, 2020

Conversation

jbytheway
Copy link
Contributor

Summary

SUMMARY: Bugfixes "Fix crash related to tool usage on resumed crafts"

Purpose of change

Fixes #32786.
Fixes #32694.

Consider the following chunk of code (which is on the stacktrace from the above issues):

[&craft_components]( std::vector<item_comp> &comps ) {
item_comp &comp = comps.front();
if( item::count_by_charges( comp.type ) && comp.count > 0 ) {
int qty = craft_components.charges_of( comp.type, comp.count );
comp.count -= qty;
// This is terrible but inventory doesn't have a use_charges() function so...
std::vector<item *> del;
craft_components.visit_items( [&comp, &qty, &del]( item * e ) {
std::list<item> used;
if( e->use_charges( comp.type, qty, used, tripoint_zero ) ) {
del.push_back( e );
}
return qty > 0 ? VisitResponse::SKIP : VisitResponse::ABORT;
} );
craft_components.remove_items_with( [&del]( const item & e ) {
for( const item *it : del ) {
if( it == &e ) {
return true;
}
}
return false;
} );
} else {
int amount = craft_components.amount_of( comp.type, comp.count );
comp.count -= amount;
craft_components.use_amount( comp.type, amount );
}
return comp.count <= 0;
} ), ret.components.end() );

Line 972 is using visitable<inventory>::charges_of, which includes the following:

template <>
int visitable<inventory>::charges_of( const std::string &what, int limit,
const std::function<bool( const item & )> &filter,
std::function<void( int )> visitor ) const
{
if( what == "UPS" ) {
int qty = 0;
qty = sum_no_wrap( qty, charges_of( "UPS_off" ) );
qty = sum_no_wrap( qty, static_cast<int>( charges_of( "adv_UPS_off" ) / 0.6 ) );
return std::min( qty, limit );
}
const auto &binned = static_cast<const inventory *>( this )->get_binned_items();

That relies on a cache of items binned by type in inventory. That binned cache keeps item* to the inventory contents hanging around.

Back in the first snippet, line 983 calls visitable::remove_items_with, which (potentially) deletes some items from the inventory. However, it doesn't invalidate the binning cache, so dangling pointers can remain.

These lines are within a loop, so the next time around the loop visitable<inventory>::charges_of uses the cache again, and dereferences an invalid pointer in a call to item::is_tool here:

if( e->is_tool() ) {

Describe the solution

Invalidate the inventory binning cache in visitable<inventory>::remove_items_with.

Add a regression test.

Testing

Ran new regression test under ASan before and after fix. It finds the use-after-free without the fix, and it's gone with the fix.

@jbytheway jbytheway changed the title Test and fix inventory caching bug Fix inventory caching bug causing crash when resuming craft Jan 7, 2020
@ZhilkinSerg ZhilkinSerg added <Bugfix> This is a fix for a bug (or closes open issue) [C++] Changes (can be) made in C++. Previously named `Code` Code: Tests Measurement, self-control, statistics, balancing. Crafting / Construction / Recipes Includes: Uncrafting / Disassembling labels Jan 7, 2020
@ZhilkinSerg ZhilkinSerg merged commit 168f48f into CleverRaven:master Jan 7, 2020
@jbytheway jbytheway deleted the resume_craft_crash branch January 7, 2020 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
<Bugfix> This is a fix for a bug (or closes open issue) [C++] Changes (can be) made in C++. Previously named `Code` Code: Tests Measurement, self-control, statistics, balancing. Crafting / Construction / Recipes Includes: Uncrafting / Disassembling
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Segfault from is_tool upon restarting craft Segmentation fault when restarting craft with ruined components
2 participants