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

Prevent use of stale pointers in item_location #31796

Merged

Conversation

jbytheway
Copy link
Contributor

Summary

SUMMARY: Infrastructure "Prevent use of stale pointers in item_location, which has been a source of various strange bugs over the years"

Purpose of change

item_location is used to store references to items in activities and various other contexts.

It was primarily based on an item* member, which was quite unsafe as items could move or be destroyed mid-activity (especially resumable activities like butchering).

You can see an example of the simplest sort of way this could go wrong in the item_location_doesnt_return_stale_map_item test I added on this PR.

We need a safer way to keep track of items.

Describe the solution

Create a new safe_reference class template which automatically invalidates itself if a targeted object is destroyed or assigned to.

Store a safe_reference<item> instead of a raw item* in item_location.

Incidentally, this makes checking the validity of item_locations much more performant than it used to be.

Describe alternatives you've considered

This safe_reference is built on shared_ptr, but using it in a somewhat unorthodox way. The more normal thing to have done would be to store all items using shared_ptrs, but I don't think that's actually a good idea, and it would heave required massive surgery to the entire codebase, whereas this is a minimal change.

Additional context

With this change, I believe item_location is now memory-safe.

However, it's still not doing the right thing in all cases.

It can end up pointing to the wrong item through a save-load cycle because of the lazy loading used after load. To fix that we need to ensure all item_location objects get unpacked before any items move after game load. @ifreund suggested that we don't need lazy loading at all, and we could just resolve all these items as the item_locations are deserialized. Given how widely item_location is used, it would be hard to verify that that would be safe.

Alternatively, we could do a pass over all item_location objects after the game is loaded and unpack them all then. But even that probably isn't enough. Consider for example how to handle NPCs who are mid-activity when their map gets unloaded. How do we fix that all up properly when the map is reloaded? Because the NPC object isn't serialized but the map is.

In any case, that's all out of scope for this PR.

Previously the implementations in impl were essentially the ones for
impl::nowhere.  Move those to nowhere specifically, and have pure
versions for impl.

Also, add some debugmsg warnings for misuse of the nowhere locations.
This is a way to keep a reference to a object which is automatically
invalidated if that object is destroyed or assigned-to.
This replaces the item* therein and ensures that item_locations cannot
end up using dangling pointers to items that no longer exist or have
moved.

Activities that try to do so should now cleanly segfault rather than
cause potentially weird behaviour (like butchering non-corpses).

As a side benefit, we get a much neater implementation of
item_location::impl::valid().
This test failed before the changes to use safe_reference.
@ifreund ifreund added [C++] Changes (can be) made in C++. Previously named `Code` Code: Infrastructure / Style / Static Analysis Code internal infrastructure and style Inventory / AIM / Zones Inventory, Advanced Inventory Management or Zones labels Jun 24, 2019
Copy link
Contributor

@ifreund ifreund left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this, it would have been way messier if I was the one implementing it. Making a wrapper for the weak and shared pointer trick was a great idea.

I'm still thinking that we may not need the lazy loading, though you're right that it's super difficult to validate. As I pointed out earlier, it seems that vehicle item_locations already depend on the map being loaded to be successfully deserialized.

@@ -81,50 +119,33 @@ class item_location::impl
}

virtual tripoint position() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this and other functions in item_location::impl::nowhere need to be marked as override

@ifreund
Copy link
Contributor

ifreund commented Jun 24, 2019

Jenkins Rebuild

@kevingranade kevingranade merged commit 42fb5dc into CleverRaven:master Jun 25, 2019
@jbytheway jbytheway deleted the item_location_stale_pointers branch June 25, 2019 18:19
ifreund added a commit to ifreund/Cataclysm-DDA that referenced this pull request Jun 26, 2019
After CleverRaven#31796 item processing invalidates item_locations, which
causes issues with all activities using item_locations.

This commit rewrites active_item_cache to store a safe_reference<item>
instead of an item_stack::iterator, removing the now unnecessary
active_item_set and has() functions.

This also fixes a bug with active item reording after processing introduced
by CleverRaven#31406 and a minor bug with the submaps_with_active_items cache.
ifreund added a commit to ifreund/Cataclysm-DDA that referenced this pull request Jun 26, 2019
After CleverRaven#31796 item processing invalidates item_locations, which
causes issues with all activities using item_locations.

This commit rewrites active_item_cache to store a safe_reference<item>
instead of an item_stack::iterator, removing the now unnecessary
active_item_set and has() functions.

This also fixes a bug with active item reordering after processing introduced
by CleverRaven#31406 and a minor bug with the submaps_with_active_items cache.
kevingranade pushed a commit that referenced this pull request Jun 27, 2019
* Rework active_item_cache to use safe references

After #31796 item processing invalidates item_locations, which
causes issues with all activities using item_locations.

This commit rewrites active_item_cache to store a safe_reference<item>
instead of an item_stack::iterator, removing the now unnecessary
active_item_set and has() functions.

This also fixes a bug with active item reordering after processing introduced
by #31406 and a minor bug with the submaps_with_active_items cache.

* Make changes from code review

In particular increment the iterator properly in active_item_cache::get()

* Remove redundant get() calls on safe references

clang-tidy told me what to do
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[C++] Changes (can be) made in C++. Previously named `Code` Code: Infrastructure / Style / Static Analysis Code internal infrastructure and style Inventory / AIM / Zones Inventory, Advanced Inventory Management or Zones
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants